home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
176-200
/
disk_188
/
execdis
/
dis.data
< prev
next >
Wrap
Text File
|
1992-05-06
|
214KB
|
7,594 lines
@a=fc0000
****************************************************************************
* *
* Comments copyright (c) 1989 Markus Wandel *
* *
* Release date: February 3, 1989. *
* *
* The following is a complete disassembly of the Amiga 1.2 "exec", as *
* found on a kickstart disk for an Amiga 1000. Everything is shown, *
* right down to the padding introduced by the linker, and unused code *
* fragments which probably made it in by accident. *
* *
* Thorough familiarity with the Rom Kernel Manual: Exec, with suitable *
* updates to version 1.2, and the exec subdirectory of the include files, *
* is assumed in all comments. Where existing documentation appears to *
* be inadequate, or a particular section of code is judged to be more *
* interesting than most, comments are more extensive. *
* *
* Absolutely no guarantee is made of the correctness of all information *
* supplied below, nor of its usefulness. *
* *
* Note that virtually all references to "ROM" actually refer to the *
* write-protected RAM which the kickstart disk loads into. The genuine *
* ROMs on Amiga 500 and 2000 computers may not contain exactly the same *
* code, although most of it should be similar. *
* *
* The completed disassembly file is not redistributable, and contains *
* code which is copyrighted by Commodore-Amiga, and comments which are *
* copyrighted by Markus Wandel. The source file used to make the *
* disassembly is distributable under a limited set of conditions as *
* outlined in the accompanying documentation. *
* *
* For Commodore-Amiga's copyright notice, see a few lines farther down. *
* *
****************************************************************************
; This match word is used when looking for ROMs.
@8p@w
; The ROM can be started by jumping to its base address plus 2,
; or by mapping it at location zero and resetting. Either way,
; the machine starts running at FC00D2 (in the latter case, because
; the 68000's PC is loaded from location 4 at cold start).
@d
@a?fc0008
@8p@28wGarbage.
@8p@w
; The following version number doesn't appear to be used
; from within exec.library.
@8p@w
@8p@w
; The following version number appears in the exec.library
; node, and this copy is checked to ensure it is still valid.
@8p@28wVersion.
@8p@28wRevision.
@8p@28lGarbage.
; The exec's ID string
; --------------------
@8p@,28s
@8p@28lGarbage.
; Copyright notice.
; -----------------
@8p@,45s
@8p@,43s
@8p@,24s
; Library name
; ------------
@8p@,14s
; The RomTag structure.
; ---------------------
@8p@28wRTC_MATCHWORD (start of ROMTAG marker)
@8p@28lRT_MATCHTAG (pointer RTC_MATCHWORD)
@8p@28lRT_ENDSKIP (pointer to end of code)
@8p@28bRT_FLAGS (no flags)
@8p@28bRT_VERSION (version number)
@8p@28bRT_TYPE (NT_LIBRARY)
@8p@28bRT_PRI (priority = 126)
@8p@28lRT_NAME (pointer to name)
@8p@28lRT_IDSTRING (pointer to ID string)
@8p@28lRT_INIT (execution address)
@a?fc00d0
@d
; We start running here.
@36dSet stack pointer to top of first 128K.
@d
@36dDelay loop.
@d
; If the ROM is also visible at F00000, or if there is another
; ROM there, jump there.
@36dLoad base address of ROM we're in.
@36dLoad (absolute address) F00000.
@36dAre we at F00000?
@36dIf so, don't execute the following.
@36dThis is relative, i.e. always points
12 bytes down from where we are.
@36dIf "1111" not found at F00000, then
@36dcontinue running below, else start
@36drunning at F00002.
; Set up port A on the first CIA (8520-A).
@36dSet low two bits for output.
@36dSet boot ROM off, power light dim.
; Disable interrupts and DMA.
@36dBase address of custom chip area.
@d
@36dDisable all interrupts.
@36dClear all pending interrupts.
@36dDisable all DMA.
; Set a blank, dark gray display.
@36dBPLCON0 = Blank screen.
@36dBitplane 0 data = all zeros.
@36dBackground colour = dark gray.
; Set up the Exception Vector Table. Vectors 2 through 47
; (Bus Error through TRAP #15) are all all set to the initial
; exception handler. If any exception occurs now, the screen
; will turn yellow, the power light will flash, and the computer
; will be reset.
@36dStart at address 8 (vector #2).
@36dDo 46 vectors.
@36dAddress of initial exception handler.
@36dSet one vector
@36dLoop back.
; See if the system wants a guru put up after reboot.
; This works as follows: If for some reason, a guru can't be put
; up in the normal fashion, the system writes "HELP" at location
; zero, writes the alert data (number and 32-bit parameter) at
; location $000100, and resets.
; Early in the startup code (right here), this "HELP" is checked
; for. If it is present, it is removed, and the data at location
; $000100 is loaded into registers D6 and D7. If no "HELP" is
; found, register D6 is loaded with -1. This data will later be
; put at ExecBase->LastAlert, once the ExecBase structure has been
; built. The following subroutine call does all this.
@36dCheck for "HELP" at location 0.
; Check whether there is already a valid ExecBase data structure.
; This is important, since it indicates whether we need to clear and
; reconfigure memory (wiping out recoverable RAM disks and such),
; or whether we already know the memory configuration and can leave
; it untouched.
; Note that if the machine crashed in such a way that the ExecBase
; structure got clobbered, memory will be cleared.
@36dGet pointer at location 4.
@36dCheck if it is an odd address.
@36dGo reconfigure memory if it is.
@36dAssume we are pointing to ExecBase.
@36dGet complement of ExecBase.
@36dCheck it.
@36dGo reconfigure memory if it didn't match.
@d
@36dChecksum the static part of the ExecBase
@36ddata structure.
@d
@d
@36dVerify the checksum.
@36dGo reconfigure memory if not valid.
; If we get this far, we are reasonably confident that the ExecBase
; structure is OK, and run the cold start capture code if there
; is any.
@36dGet the cold start capture vector.
@36dBranch if it is zero.
@d
@36dWhere to come back afterward.
@36dClear the cold start capture vector.
@36dJump to the cold start capture code.
; We come here if the cold start capture vector was zero, or
; upon return from the cold-start capture code. We continue
; to verify the ExecBase structure.
@36dFlip the power light to bright.
@36dCheck the version/revision numbers
@36dstored in ExecBase against those in ROM.
@36dGo reconfigure memory if no match.
@36dGet end address of chip memory.
@36dGreater than 512K?
@36dIf so, it must be invalid.
@36dLess than 256K?
@36dIf so, it must be invalid.
@36dGet end address of $C00000 memory.
@d
@36dAll OK if no $C00000 memory.
@36dCheck more than 1.5 meg (invalid).
@36dGo reconfigure memory if so.
@36dCheck if less than 256K (invalid).
@36dGo reconfigure memory if so.
@d
@36dCheck that ends on a 256K boundary.
@36dAll OK if it does.
; If we come here, it was decided that there is no valid ExecBase
; data structure. This means we have to figure out what the memory
; configuration of the machine is.
; First, calculate ExecBase based on the assumption that the ExecBase
; structure will end up in chip RAM. This would put it at $0676,
; just far enough past the exception vector table to make room for
; the jump table.
@36dCalculate $0676.
@36d(don't ask me why they do it like this).
; Now go and check for memory in the $C00000 - $DC0000 area.
; This allows for a maximum of 1.75 megabytes of non-chip memory
; to be automatically configured if located at $C00000.
@36dLower bound for $C00000 memory.
@36dHigh bound for $C00000 memory.
@36dReturn address.
@36dGo check how much we have.
@36dDid we find any expansion memory?
@36dIf not, skip the following.
; The machine has expansion RAM at $C00000. We put the ExecBase
; structure there to save chip memory. This puts it at $C00276.
@36dCalculate $C00276.
@d
; Now we clear the expansion memory to zeros.
@36dGet end address of expansion memory.
@36dGet start address of expansion memory.
@36dSet return address.
@36dGo clear the memory.
; Having figured out the end address of expansion memory (in A4),
; and the value to use for ExecBase (in A6), we now check how much
; chip memory we have. Any memory in the first 2 megabytes of
; address space is considered to be chip memory. Less than 256K
; of chip memory is considered a fatal error.
@36dStart looking at location 0.
@36dDon't look past 2 megabytes.
@36dSet the return address.
@36dGo check the memory.
@36dDo we have at least 256K of chip memory?
@36dBomb if not.
; Clear chip memory. Everything from $C0 (right after the end of
; the initial exception vector table we've set up) to the end of
; chip memory is cleared.
@36dClear location 0.
@d
@36dSet start address to $C0 (end of vectors)
@36dSet return address.
@36dGo clear the chip memory.
; Since we have found less than 256K of chip memory, some of it
; must not be working. Turn the screen bright green, blink the
; power light, and reset.
@d
@d
; We continue here after we've figured out where the chip memory
; ends (256K or greater) and where the $C00000 memory ends
; (0 if none present). The two addresses are in A3 and A4,
; respectively.
@36dPoint to base of custom chip area.
@36dDisable all DMA.
@36dSet BPLCON0 for a blank screen.
@36dSet bitplane 0 data to zeros.
@36dSet background colour to medium gray.
; Clear most of the ExecBase structure to zeros.
@36dPoint at ExecBase->IntVects.
@36dGet KickMemPtr, KickTagPtr, KickCheckSum.
@d
@36dClear all of ExecBase from IntVects
@36dto the end of the structure.
@d
@36dRestore Kick variables saved above.
; Set up the ExecBase pointer at location 4, and its complement
; in the ExecBase structure.
@36dInstall ExecBase pointer at location 4.
@d
@d
@36dInstall ExecBase complement check value.
; Set up the system stack.
@36dTry to put stack in $C00000 RAM.
@36dDo we have any $C00000 RAM?
@36dIf not, use chip RAM.
@36dSet system stack pointer.
@36dStore system stack upper bound.
@36dAllow 6K bytes for system stack.
@36dStore system stack lower bound.
; Store the memory configuration. Next reset will use this if
; still intact, and not clear memory.
@36dStore top of chip memory.
@36dStore top of $C00000 memory.
; Part 2 of the deferred-guru procedure. Long ago, we set up
; registers D6 and D7 with the data for ExecBase->LastAlert.
; The following call writes them there. Now all is ready for
; the "alert.hook" mechanism to put up the deferred guru, if
; one was wanted.
@36dSetup ExecBase->LastAlert.
@36dCheck CPU type, and if 68881 present.
@36dOr the result into the Attention flags.
; Initialize the exec lists. This is driven from a data table which
; contains the offsets from ExecBase where the various lists are,
; and the list types.
@36dPoint to the table.
@36dGet a table entry.
@36dZero marks the end.
@36dAdd to ExecBase to get absolute address.
@36dClear the list by setting its head
@36dpointer to point to itself,
@36dclearing its "Tail" field,
@36dand setting up the "TailPred" pointer.
@36dGet the list type.
@36dPut it into the list header.
@36dLoop back to do next list.
@a?fc02d2
; Table of list header offsets and types.
@8p@5w@11wMemList (ExecBase + $142, type = NT_MEMORY)
@8p@5w@11wResourceList (ExecBase + $150, type = NT_RESOURCE)
@8p@5w@11wDeviceList (ExecBase + $15E, type = NT_DEVICE)
@8p@5w@11wLibList (ExecBase + $17A, type = NT_LIBRARY)
@8p@5w@11wPortList (ExecBase + $188, type = NT_MSGPORT)
@8p@5w@11wTaskReady (ExecBase + $196, type = NT_TASK)
@8p@5w@11wTaskWait (ExecBase + $1A4, type = NT_TASK)
@8p@5w@11wIntrList (ExecBase + $16C, type = NT_INTERRUPT)
@8p@5w@11wSoftInts[0] (ExecBase + $1B2, type = NT_SOFTINT)
@8p@5w@11wSoftInts[1] (ExecBase + $1C2, type = NT_SOFTINT)
@8p@5w@11wSoftInts[2] (ExecBase + $1D2, type = NT_SOFTINT)
@8p@5w@11wSoftInts[3] (ExecBase + $1E2, type = NT_SOFTINT)
@8p@5w@11wSoftInts[4] (ExecBase + $1F2, type = NT_SOFTINT)
@8p@5w@11wSemaphoreList (ExecBase + $214, type = NT_SIGNALSEM)
@8p@16wEnd of table marker.
; Table used to initialize the exec's library node.
@8p@16bType = NT_LIBRARY.
@8p@16bPriority = 0.
@8p@16lName = pointer to "exec.library".
@8p@16bFlags = LIBF_CHANGED | LIBF_SUMUSED.
@8p@16bPad.
@8p@16wNegSize (not set yet).
@8p@16wPosSize.
@8p@16wVersion.
@8p@16wRevision.
@8p@16lIdString = pointer to "exec ..."
@8p@16lChecksum (not set yet).
@8p@16wOpenCnt = 1.
; Names to use in the system free-memory lists.
@8p@,12s
@8p@,12s
@36dGet address of task crash routine.
@36dInstall in default trap code.
@36dInstall in default exception code.
@a?fc034a
@40dSet the default task exit address.
@40dPreallocate the lower 16 signals.
@40dPreallocate TRAP #15 (for use
in ROM-Wack breakpoints).
@36dInitialize the exec.library node
@36dup to and including the OpenCnt
@36dfield, from the table above.
@d
@d
@36dMake the library jump vector, using the
@36dtable at $FC1A40, and the MakeFunctions()
@36droutine. Setting A2 to the table address
@36dsignals that the table is relative.
@36dInstall library negative size.
@36dSee if we have expansion memory.
@36dBranch past this if we don't.
; Add expansion memory at $C00000 to the free memory lists.
@36dFirst free location = ExecBase + $024C.
@36dName = "Fast Memory".
@36dPut in memory list at priority 0.
@36dAttributes = MEMF_FAST | MEMF_PUBLIC.
@36dGet end address of expansion memory.
@36dSubtract address of first free location.
@36dSubtract system stack size.
@36dBuild free list and add it to system.
@36dFree chip memory starts at $0400.
@36dSpace for stack already reserved.
@d
; Add chip memory to free memory lists. Enter here if there is
; no expansion memory, and ExecBase therefore resides at the bottom
; of chip memory.
; Note how chip memory is added to the system list at a lower
; priority than expansion memory. This causes it to be allocated
; only when specifically requested or when expansion memory is full.
@36dFree chip memory is at ExecBase + $024C.
@36dReserve 6K for system stack.
; Enter here if we do have expansion memory, with D0 and A0 set
; up as above.
@36dAttributes = MEMF_CHIP | MEMF_PUBLIC.
@d
@36dName = "Chip Memory".
@36dPriority = -10.
@36dGet end address of free chip memory.
@36dSubtract address of first free location.
@36dBuild free list and add it to system.
@36dGet ExecBase.
@36dAdd the exec to the system library list.
; Set the exception vector table up for actual system operation.
; Up to this point, any interrupt or exception would have caused
; the screen to turn yellow and the computer to reset.
; The format of the data table used here is explained in comments
; at the front of the data table.
@36dPoint to data table.
@36dSet up base address for the offsets.
@36dPoint to exception vector #2.
@36dEnter the loop at the bottom.
@36dConvert table entry to absolute address.
@36dStore the handler address in the EVT.
@36dGet the next data table entry.
@36dLoop until end of table reached.
@36dSee if we are running on a 68010/020.
@d
@36dSkip the following if not.
; Special initialization for machines using a 68010/020.
@36dPoint at 68010/020 bus error handler.
@d
@36dFix the bus error vector.
@36dFix the address error vector.
@36d Use a different Supervisor() routine.
; Fix GetCC() for 68010/020 processors.
; We simply load the instruction sequence "MOVE.W CCR,D0 / RTS" into
; the place where the library jump vector to GetCC() normally is.
@d
; Check if we have a 68881 numeric coprocessor, and if so, fix up
; some more vectors. This needs to be done since we also need to
; save the 68881's context when we switch tasks.
@40dDo we have a 68881? If so,
@d
@40dUse a different Switch() function.
@40dUse a different Dispatch() funciton.
; Regular 68000's continue here.
@36dInitialize the exec interrupt handlers.
@36dPoint to the custom chips.
@36dEnable all DMA.
@36dEnable the interrupt system.
@36dSet the interrupt disable level to -1.
@36dInitialize ROM-Wack.
@36dChecksum the static part of the ExecBase
@36ddata structure.
@36dThis is used after a reset to see if
@36dthe data structure has been clobbered.
@d
@d
@36dStore the checksum.
; Now we are going to manufacture the very first task.
; We use AllocEntry() to obtain a block of memory. This is then
; used to hold the MemList from AllocEntry(), the task's stack,
; and the task descriptor.
@36dPoint to the MemList.
@36dAllocEntry()
@36dGet the address of the MemList.
; It's assumed here that the allocated memory follows directly
; after the MemList. A safe assumption, since we still have
; unfragmented memory. We now create a task descriptor at the
; top of the allocated memory. The stack pointer for the task
; is initialized below the task descriptor.
@36dPoint near the top of the memory block.
@36dPoint to the future task descriptor.
@d
@36dStore stack lower bound.
@36dStore stack upper bound.
@36dSet the initial stack pointer image.
@36dSet the stack pointer itself.
@36dPriority = 0.
@36dNode type = NT_TASK.
@36dTask name = "exec.library".
; We initialize the task's memory list to empty, then enqueue
; the MemList holding all this memory there. This means that
; when the task dies, the memory will automatically be deallocated.
@36dPoint to the task's memory list.
@36dInitialize it to empty.
@d
@d
@d
@36dEnqueue the MemList from AllocEntry()
@36don the task's memory list.
@36dGet the task address back.
; Make this the current task, and make it ready to run.
; initialPC and finalPC are both initialized as zero, but no
; harm results, since the task can't start running yet.
@36dMake this the current task.
@36dClear A2.
@36dClear A3.
@36dAddTask()
@36dGet the pointer to the task again.
@36dMake the task state TS_RUN.
@36dUnlink it from the TaskReady queue.
; A historic moment: We turn the supervisor mode flag off.
; Starting right now, we are running as a task named "exec.library",
; and the multitasking system is operational.
@a?fc04be
FC04BE and.w #0,SR Turn the supervisor bit off.
@a=fc04c2
@36dForbid()
@36dPermit()
@d
; The MemList used to allocate memory for the initial task.
@7p@9l@9l@9l@8wA dummy list node.
@7p@35w1 block of memory desired.
@7p@35lMEMF_PUBLIC | MEMF_CLEAR
@7p@35l1124 bytes.
; Table of areas to look for RomTags in. I don't know why the
; FC0000 - 1000000 area is covered twice. The F00000 to F80000
; area appears to be an alternate or additional place to put ROMs.
@7p@9l@l
@7p@9l@l
@7p@9l@l
@7p@35lEnd of list marker.
; Scan for RomTags, process the KickMemPtr and KickTagPtr
; variables, and build a table of all the resident modules found.
; The address of the table of resident modules is stored in
; the ExecBase data structure.
@36dPoint to table of ROM address spaces.
@36dScan for RomTags, etc.
@36dStore the result in ExecBase->ResModules.
@36dSet the power light to bright.
; Handle the "cool start" capture vector. Note that if we decided
; (much) earlier that ExecBase had been clobbered, it will have
; been rebuilt from scratch, and the cool start capture vector
; will be zero. Thus, we don't have to verify it further.
@36dGet the "cool start" capture vector.
@36dBranch past the following if zero.
@d
@36dCall the "cool start" capture code.
; Another historic moment. We call InitCode() to initialize the
; resident modules. This is where all the other stuff in the ROMs,
; stuff in RAM which survived the reboot, etc. comes online. We
; indicate that all those modules with the RTF_COLDSTART flag set
; should be initialized now.
@36dRTF_COLDSTART flag must be set.
@36dMinimum version is 0 (any will do).
@36dInitCode().
; Yet another capture vector, this time the "WarmCapture" one.
@36dCheck the "WarmCapture" vector.
@36dBranch past this if zero.
@d
@36dCall the warm start capture code.
; I assume that when the DOS came online, it took over. This
; task looks like it's heading into a dead end.
; Clear all the CPU registers except for ExecBase and the stack
; pointer.
@38dPush 14 longwords of zero on
@38dthe stack.
@d
@38dRead them off again into the registers.
; This is the end of the road.
Do forever
@38dDebug()
@38dGet ExecBase.
@36dEnd
; Determine CPU type and whether FPP is present.
; ----------------------------------------------
; We need to know whether a non-68000 CPU is present for two
; reasons: First, on the 68000, at least one instruction
; (MOVE.W SR,<ea>) is available in user mode, whereas on the
; newer CPU's, it is privileged. Second, on the newer CPU's,
; when an exception occurs, more information is saved on the
; stack, and in the case of a bus error, the CPU's entire state
; is dumped there so that virtual memory computers (big UNIX
; boxes for example) can recover from page faults.
; Note that the 68020 can do everything the 68010 can, and thus,
; if a 68020 is detected, both the AFB_68010 and the AFB_68020
; flags will be set.
; We need to know whether there's an FPP present since, for task
; switches, we want to save the FPP registers on the stack as
; as the CPU registers, so each task can think it has the FPP
; all to itself.
@d
; We are going to try 68010/020 and 68881 instructions. These will
; cause error exceptions if the respective parts aren't present, so
; we set up to trap these.
@36dSave "Illegal Instruction" error vector.
@36dSave "1111 Opcode" error vector.
@36dPoint to temporary exception handler.
@36dInstall this address in both vectors.
@d
@36dSave the stack pointer.
; Initialize the flags to zero (D0), and point to address zero (D1).
; Then we try to set the 68010 Vector Base Register, which determines
; where the exception vector table is. In the 68000, this is hard
; wired at zero. If the 68010 is present, we set it to zero.
@d
@d
@a?fc0564
FC0564 movec D1,VBR Set Vector Base Register to 0.
@a=fc0568
; If we're still here, the CPU is at least a 68010. We thus set
; the AFB_68010 flag. Then we try to access a 68020-specific
; feature, namely, we try to enable its instruction cache.
@36dSet AFB_68010 flag.
@d
@a?fc056e
FC056E movec D1,CACR Try enabling the 68020 cache.
@a=fc0572
; If we're still here, we have a 68020, and so we set the AFB_68020
; flag. Then we see if we also have a 68881 FPP, by trying to
; access one of its registers. Note that this will not cause an
; exception if no FPP is present.
@36dSet AFB_68020 flag.
@a?fc0576
FC0576 fmove.l FPCR,D1 Try reading a 68881 register.
@a=fc057a
@36dDid it work?
@d
@36dIf so, set the AFB_68881 flag.
; We continue here either from above, or, on plain Amigas, by
; an error exception from one of the foreign instructions above.
; D0 contains all the flags which have been set along the way.
; We restore the two changed entries in the EVT and the stack
; pointer, then exit.
@36dRestore the stack pointer.
@36dRestore the exception vectors.
@d
@d
@d
; Chip Memory Checking Routine
; ----------------------------
; This routine checks for the presence of memory. It is used at
; startup to determine how much chip memory is available. Note
; that it can't be used to check for memory at $C00000, and a
; special routine is provided to do this further on.
; On entry, A0 is the lower bound of the area to check, and A1 is
; the high bound. Memory is checked in 4K blocks.
@d
@40dWrite a zero to the first location.
@40dSave the first location.
@40dUse this as a signature value.
; Main loop: We enter here to check each 4K block.
@40dIncrement current location by 4K.
@40dSee if upper bound reached.
@40dIf so, exit from the loop.
@40dWrite the signature into memory.
; Longword 0 of the block being checked was initially cleared to
; zero. If it is now no longer zero, we have "wrapped around",
; i.e. due to incomplete address decoding, we have written the
; signature value at the beginning of the block. When this
; occurs, we have reached the end of memory, even though the
; signature value would read back correctly.
@40dCheck location 0.
@40dExit if signature appears there.
@40dSee if signature can be read back.
@40dIf successful, go check more memory.
; Done, return the end address of memory to the user. Return
; via indirect jump through A5 since we don't have a stack yet.
@d
@d
; Error System Reset Routine
; --------------------------
; This is the routine which blinks the power light, then resets
; the computer. It is called from the startup code if a failure
; of any sort is detected. The colour of the screen indicates
; the type of failure.
; This is the exception entry point. All vectors in the
; Exception Vector Table point here while the ROM kernel is
; initializing itself. A yellow screen means an unexpected
; exception has occurred.
@40dColour number for yellow.
; This is the non-exception entry point. From here on down it's
; a general purpose routine which can be entered with a coulour
; number in D0.
@40dPoint to the custom chips.
@40dSet BPLCON0 for a blank screen.
@40dSet bitplane 0 data to zeros.
@40dSet background colour to yellow.
@40dFor D1 = 1 to 10 do
@42dSet delay to 1 time unit.
@42dMake power light dim.
@42dDelay.
@42dSet delay to 0.5 time unit.
@42dMake power light bright.
@42dDelay.
@40dEndfor
; Note: The "boot" and "ig" commands from ROM-Wack jump here.
@40dDelay some more.
@d
@d
@40dReset everything external to the CPU.
; Get the initial PC from the ROM (now mapped at zero due to the
; reset instruction) and start over.
@d
@d
; Memory Clear Subroutine
; -----------------------
; This subroutine clears a block of memory. The start address is
; in A0, the end address is in D0. Since we may not have a stack,
; the return address is provided in A5.
@40dValue to store in memory.
@40dCompute number of bytes to clear.
@40dDivide by 4 (number of longwords).
@40dPut the low-order 16 bits in D0,
@40dand the high-order ones in D1, and
@40dstart at the bottom (for dbra's).
@40dClear a longword.
@40dLoop until current 256K block done.
@40dLoop until everything done.
@40dReturn to caller.
; $C00000 Expansion RAM Checker
; -----------------------------
; The following routine checks for the presence of memory
; in the $C00000 - $DC0000 area. This is a nontrivial exercise,
; since if there is no memory there, we see images of the custom
; chip registers there instead, due to incomplete address decoding.
;
; This took a while to figure out, so I'm commenting it
; very heavily for my own satisfaction.
; Register A4 holds the end address of the block where we know
; RAM to reside. At first, we initialize this to the start address.
; Then, each time through the loop, we copy A4 to a temporary
; register, which we increment by 256K.
@40dCopy start address into A4.
@40dCopy A4 into temporary register.
@40dAdd 256K to temporary register.
; Now we write to an address $0F66 bytes less than the temporary
; register. If there is RAM here, this will write into it near
; the top of the 256K block. If there isn't, this will write to
; the INTENA register and disable all interrupts.
@40dWrite to RAM or INTENA.
; Now we read an address $0FE4 bytes below the temporary address.
; If there is memory here, this will read it. Otherwise, it will
; read the INTENAR register. If we find a non-zero value, it must
; be memory, since all bits in INTENAR were reset above. Otherwise,
; it could be either (memory could happen to contain zero).
@40dRead RAM or INTENAR.
@40dIf not zero, we've found memory.
; We got a zero. Make sure this isn't INTENAR by causing bits
; to be set in it. We set all of them except for the master
; interrupt enable. Again, if there's RAM here, this won't do
; anything to INTENAR, and we'll continue to see a zero.
@40dWrite to RAM or INTENA.
; Read the same location as before. If this returns anything but
; $3FFF, it's fine. $3FFF means we're seeing INTENAR. If it's
; still zero, it's RAM. Anything else would be a fatal error, but
; that isn't checked for.
@40dRead RAM or INTENAR.
@40dExit from loop if INTENAR seen.
; Now we bump up A4 by copying the temporary register back. This
; means we have found a valid 256K block. We then go on looking
; until we've reached the upper limit of the address space to check.
@40dUpdate A4 (means memory was found).
@40dCompare to upper limit.
@40dKeep looking if not reached.
; Continue here when we've reached the end of the space to check,
; or finally seen an image of the INTENAR register. We return the
; end address of the detected RAM in A4, or zero A4 if no RAM
; was found.
@40dDisable all interrupts.
@40dWas A4 ever updated?
@40dIf so, return it.
@40dZero A4 (indicates no memory).
@40dReturn to caller.
@8p@32wPadding
---------------------------------------------------------------------------
AddDevice( device )
A1
---------------------------------------------------------------------------
@36dPoint to the system device list.
@36dAdd the device to the list.
@36dUpdate the device's vector checksum.
@d
---------------------------------------------------------------------------
RemDevice( device )
A1
---------------------------------------------------------------------------
; NOTE: Some other part of the system, presumably DOS, will patch
; itself in ahead of this.
@36dJust go to RemLibrary().
---------------------------------------------------------------------------
error = OpenDevice( devName, unitNumber, ioRequest, flags )
D0 A0 D0 A1 D1
---------------------------------------------------------------------------
; NOTE: Some other part of the system, presumably DOS, will patch
; itself in ahead of this, presumably so it can pull devices off
; disk if they aren't already in the device list.
@d
@36dPoint to the ioRequest.
@36dClear the io_Error field in it.
@d
@d
@36dPoint to the system device list.
@36dForbid()
@36dFindName()
@36dGet the pointer to the device.
@d
@36dStore it in the ioRequest.
@36dReturn -1 if device not found.
@36dClear the io_Unit pointer.
@d
@36dSave ExecBase.
@36dPoint A6 to the device's base address.
@36dCall the device's Open() function.
@36dRestore ExecBase.
@36dGet the io_Error (from the Open() call).
@36dExtend to a longword for return to
@36dthe caller.
@36dPermit()
@d
@d
; Continue here if the device was not found.
@36dSet the return value to -1.
@36dSet the io_Error field to -1.
@d
---------------------------------------------------------------------------
CloseDevice( ioRequest )
A1
---------------------------------------------------------------------------
; NOTE: Some other part of the system, presumably DOS, will patch
; itself in ahead of this.
@36dForbid()
@36dSave ExecBase.
@36dGet the pointer to the device node.
@36dCall the device's Close() function.
@36dRestore ExecBase.
@36dPermit()
@d
---------------------------------------------------------------------------
SendIO( ioRequest )
A1
---------------------------------------------------------------------------
; The command is sent to device with the "quick I/O" bit not set.
; This means the device must always respond by sending the
; I/O request back as a reply message when done.
@36dClear the "quick I/O" bit.
@36dSave ExecBase.
@36dGet pointer to device node.
@36dCall the device's "BeginIO" entry point.
@36dRestore ExecBase.
@d
---------------------------------------------------------------------------
error = DoIO( ioRequest )
D0 A1
---------------------------------------------------------------------------
; The command is sent to the device with the "quick I/O" bit set.
; This allows the device to decide whether to process the request
; synchronously or asynchronously.
@36dSave ioRequest pointer for later.
@36dSet the "quick I/O" bit.
@36dSave ExecBase.
@36dGet pointer to device node.
@36dCall the device's "BeginIO" entry point.
@36dRestore ExecBase.
@36dGet ioRequest pointer back.
; Now fall through to WaitIO.
---------------------------------------------------------------------------
error = WaitIO( ioRequest )
D0 A1
---------------------------------------------------------------------------
; If the "quick I/O" bit is set, then the call to the device
; in BeginIO() finished the I/O operation synchronously, and
; we can return to the caller. Otherwise, we must wait for
; the reply message from the device.
@36dCheck the "quick I/O" bit.
@36dIf still set, the I/O is complete.
; "quick I/O" bit was not (or no longer) set, so we need to
; wait for the reply message from the device.
@36dSave A2.
@36dSave pointer to the ioRequest.
@36dGet pointer to the reply port.
@36dGet the reply port's signal bit.
@d
@36dConvert to signal mask.
@36dDisable()
@d
@36dCheck if the ioRequest became NT_REPLYMSG
@d
@36dWait for the signal.
@36dGo back and check the ioRequest again.
@36dUnlink the ioRequest from the reply
@36dport's message queue.
@d
@d
@d
@36dEnable()
@d
@d
@36dRestore pointer to the ioRequest.
@36dRestore A2.
@36dGet the error field from the ioRequest.
@36dExtend to long word.
@d
@d
---------------------------------------------------------------------------
result = CheckIO( ioRequest )
D0 A1
---------------------------------------------------------------------------
@a?fc074e
@36dCheck the "quick I/O" bit.
@36dIf set, request is done, so...
@36dReturn address of ioRequest.
@d
@36dCheck if ioRequest type is NT_REPLYMSG
@36dIf so, it is finished.
@36dReturn zero.
@d
@36dReturn address of ioRequest.
@d
---------------------------------------------------------------------------
AbortIO( ioRequest )
A1
---------------------------------------------------------------------------
@36dSave ExecBase.
@36dGet pointer to the device node.
@36dCall the device's "AbortIO" entry point.
@36dRestore ExecBase.
@d
; Table used to set up the exception vector table.
; Each entry is a 16-bit offset from the table's base address
; to the address of the exception handler for that exception.
; The final longword of zeros marks the end of the table.
; The first entry means that the handler for exception number 2
; (bus error) is at $FC0778 + $64. The next entry is for exception
; number 3, and so on through to the end of the TRAP instruction
; vectors.
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@l
@7p@29wEnd of table marker.
@7p@29wPadding.
; Exception entry points. The indicated entries in the Exception
; Vector Table are set to point here.
; The BSR instructions are used so that the stacked return address
; from the BSR can be used to find out which exception occurred.
@d
@d
@36dBus error.
@36dAddress error.
@36dIllegal instruction.
@36dDivide by zero.
@36dCHK instruction.
@36dTRAPV instruction.
@d
@36dTrace mode.
@36d"1010" opcode.
@36d"1111" opcode.
@36dReserved vector #12.
@36dReserved vector #13.
@36dReserved vector #14.
@36dReserved vector #15.
@36dReserved vectors #16-23, spurious int.
@36dTRAP #0
@36dTRAP #1
@36dTRAP #2
@36dTRAP #3
@36dTRAP #4
@36dTRAP #5
@36dTRAP #6
@36dTRAP #7
@36dTRAP #8
@36dTRAP #9
@36dTRAP #10
@36dTRAP #11
@36dTRAP #12
@36dTRAP #13
@36dTRAP #14
@36dTRAP #15
; Handler for reserved exceptions #16-23 and spurious interrupts.
; These are dead ends (click for Guru).
@a?fc081a
FC081A or.w #$0700,SR Disable all maskable interrupts.
@a=fc081e
@36dAlert number (fatal).
@36dFreeze the task.
; Another exception handler. This one is a dead end also.
@a?fc0828
FC0828 or.w #$0700,SR Disable all maskable interrupts.
@a=fc082c
@d
@36dMake alert number.
@36dFreeze the task.
; Handler for bus and address errors (long stack frame).
; These two can be caught by the task if set up to do so.
@36dUse the return address on the stack
@36dto compute the exception number.
@36dSee if error occurred in supervisor mode.
@36dIf not, go to task's trap routine.
@36dFreeze the task.
; Handler for miscellaneous other errors.
; These can be caught by the task.
@36dUse the return address on the stack
@36dto compute the exception number.
@36dSee if error occurred in supervisor mode.
@36dIf not, go to task's trap routine.
@36dGuru time if in supervisor mode.
; Handler for TRAP instructions.
@36dUse the return address on the stack
@36dto compute the exception number.
@36dSee if error occurred in supervisor mode.
@36dGuru time if in supervisor mode.
@36dElse go to task specific trap routine.
; Bus error handler for 68010/020 processors. These processors
; produce a more detailed stack frame when they hit a bus error
; (for use in virtual memory systems). The bus and address
; error exception vectors are set to point here at system startup
; if such a processor is being used.
@36dPush a zero from the stack.
@36dRead the exception number.
@36dConvert it to a format compatible
@36dwith the numbering scheme used
@36delsewhere.
@36dProcess the exception normally.
; We get here if various exceptions occurred in user mode.
; This looks up an exception handler address in the current task's
; descriptor, and jumps there. This allows each task to specify
; what is to happen if it causes an exception or executes a
; TRAP instruction.
; The task can clean up the stack and continue running if it
; wants to. All its registers are preserved.
@36dReserve 4 words on stack and save A0.
@36dGet ExecBase
@36dGet the current task pointer.
@36dGet current task's tc_TrapCode address
and put it on the stack for the RTS.
@36dRestore A0.
@d
---------------------------------------------------------------------------
Supervisor( code_to_execute )
A5
---------------------------------------------------------------------------
; This routine is used to run things with the CPU in supervisor
; mode. The address of the code to execute once we're in supervisor
; mode is passed in A5.
; First, we tamper with the status register (attempt to set the
; supervisor mode bit). If successful, we are already in supervisor
; mode. If not, there will be a privilege violation, handled by
; the exception handler below.
@a?fc08aa
FC08AA or.w #$2000,SR Tamper with the status register.
@a=fc08ae
; Are we still here? If so, we are already in supervisor mode.
; Fake an exception by pushing status register and PC on the stack.
@36dPush a fake program counter.
@36dPush the status register.
@36dGo execute the caller's code.
@d
; The following is 68010/020 version of the above. It is used
; if the startup code has detected such a processor and pointed
; the Supervisor() vector here.
@a?fc08ba
FC08BA or.w #$2000,SR Tamper with the status register.
@a=fc08be
@36dMake room on the stack.
@36dStore status register.
@36dStore fake program counter.
@36dStore 68010/020 stack frame type.
@36dGo execute the caller's code.
; Privilege violation exception handler.
; First, we find out where the CPU was when it got the privilege
; violation.
@36dWas it the 68000 Supervisor() function?
@d
@36dWas it the 68010/020 version?
@d
@36dYes, fix return address, and go
@36dexecute the caller's code.
; If we get here, it was a real privilege violation, i.e. not one
; of the intentionally caused ones above.
@a?fc08f0
FC08F0 or.w #$0700,SR Disable all maskable interrupts.
@a=fc08f4
@36dMake alert number.
@36dFreeze the current task or guru.
@8p@28wPadding.
; ROMTAG Scanner and "KickMemPtr/KickTagPtr" Processor
; ----------------------------------------------------
; The routines in this section do two things. One, they scan a
; section of the CPU addressing space for resident modules, each
; flagged by a "RomTag". For an example of what a RomTag looks
; like, check out the one at the very beginning of the exec.
; The start and end addresses of each piece of CPU address space
; to look for RomTags in are given in a table, to which A0 must
; point on entry.
; A temporary list is built, containing nodes which each point to
; a RomTag. Each resident module has a name, and no two modules
; with the same name are allowed. If there is a conflict, the
; one with the higher version number or priority wins and the
; other one is discarded.
; After the list of RomTags has been compiled, we process something
; called the KickMemPtr and the KickTagPtr, found in the ExecBase
; structure. The former points to a chain of MemLists. The latter
; points to an odd type of chain containing pointers to RomTags.
; If you really need to know the format of this chain, read the
; comments at the appropriate code.
; All the MemList structures, and tables of RomTag addresses, pointed
; to by these pointers, have a checksum, also stored in ExecBase.
; If the checksum we calculate doesn't match that checksum, we
; discard all the data (assume it has been damaged in a system crash
; before the reboot).
; If the checkum matches, then we try to allocate all the pieces
; of memory pointed to by the KickMemPtr MemLists. If this is
; successful, we add all the KickTagPtr RomTags to the RomTag list.
; Presumably, the KickMemPtr list will indicate where RAM-resident
; modules are located, and if we were able to reclaim them before
; the memory got allocated for something else, we add them to the
; system again after a reboot.
; Finally, we process the RomTag list. This takes the form of
; building a table of resident module addresses, terminated with
; a zero, in memory allocated for that purpose. The RomTag list is
; deallocated. Note that the table of module addresses has the
; same format as one of the entries in the KickTagPtr list.
; Each resident module can have a flag set in its RomTag which
; which causes it to be automatically initialized later.
@d
; First, create a temporary list structure to hold the found RomTags
; in. The list header is put on the stack.
@36dReserve 14 bytes on the stack (enough
@36dfor a list header), and get the address.
@36dClear the list to empty.
@d
@d
@d
; Now process the table of areas to look for RomTags in. Call the
; RomTag scanning routine with the data from each table entry to
; find all the RomTags in that section of address space.
@36dPoint to the start of the address table.
@36dSee if end of table reached.
@36dNegative value indicates end.
@36dGet the start address and the end address
@36dof an area to look for RomTags in.
@36dScan the area for RomTags.
@36dLoop until end of list reached.
; Now process the "Kick" variables in ExecBase. These apparently
; exist to allow a set of resident modules (such as a RRD) to
; survive a system reboot. If the checksum of all the data tables
; is still valid, we claim the memory and add the RomTags to
; the list.
@36dSumKickData()
@36dVerify the checksum against the old one.
@36dSkip the following if not valid.
@36dTry to reallocate all the MemLists.
@36dDid we get all the memory back?
@36dIf not, don't bother with the KickTags.
@36dProcess the list of "KickTags".
@36dBuild a table of RomTag addresses.
@36dDeallocate the RomTag list header.
@d
@d
; This routine scans an area indicated by A4 (start address) and
; D4 (end address) for RomTag structures, and puts any found RomTags
; on the RomTag list. If there is more than one version of any
; given RomTag, the newest one wins, the older ones are discarded.
@d
@36dLoad the RomTag matchword.
@36dGet the end address.
@36dSubtract the start address.
@36dIf less than zero, return.
@36dDivide the result by 2 (number of words).
@36dDecrement by 1 (for DBcc instruction).
@36dSplit into two halves (since DBcc can
@36donly use 16-bit counters).
@d
@36dLook for the RomTag matchword.
@d
@36dLoop until entire area scanned.
@36dExit if end reached.
; We've found a RomTag matchword. A4 points to it. Check if
; this is really a RomTag by verifying the pointer directly
; after the matchword.
@36dPoint to the matchword.
@36dSee the longword directly after the
matchword points to it.
@36dNot a valid RomTag if not.
; We are pointing to a valid RomTag. Add it to the RomTag list.
@36dAdd RomTag to list of found ones.
; Each RomTag has in it a pointer (RT_ENDSKIP) which points to
; the address where to start looking for the next RomTag, i.e.
; the end of whatever code/data is associated with it.
@36dSkip to the end of the module.
@36dStart looking for the next one.
@d
@d
; This subroutine adds a RomTag to the list of found RomTags.
; If the current RomTag has the same name as a RomTag already in
; the list, the older (or if same age, lower priority) version
; is discarded. Memory is allocated to hold each node as needed.
@36dPoint to the temporary list on the stack.
@36dGet the name of this RomTag.
@36dFindName()
@36dDid we find a node with this name?
@36dIf not, go and add the tag to the list.
@36dPoint to the node we found.
@36dCompare the version numbers of the list
@36dnode and the newly found RomTag to find
@36dout which is more recent.
@36dDiscard current RomTag if older.
@36dUse current one if newer.
@36dVersion numbers match, so compare
@36dthe priority fields.
@36dDiscard current RomTag if lower priority.
; This RomTag supersedes a previous one, which is already in the
; list. Therefore we discard the one in the list.
@36dPoint to the node in the list.
@36dUnlink it from the list.
@36dKeep a pointer to the node's memory.
@36dGo and make a new node out of it.
; We enter here if this RomTag doesn't supersede some other one.
; This means we have to allocate some memory for a list node to
; store information about it.
@36dNo particular memory requirements.
@36d14 bytes.
@36dGo and allocate the memory.
@36dDid we get the memory?
@36dIf not, skip the following.
@36dPoint to the memory.
; We have obtained 14 bytes of memory one way or another, and we
; now build a list node and add it to the list of found RomTags.
@36dCopy the RomTag's priority.
@36dCopy the RomTag's name.
@36dStore the RomTag's address.
@d
@36dAnd enqueue on the RomTag list.
@d
; RomTag list to resident module table converter.
; This routine scans the temporary RomTag list, and builds a table
; of resident module addresses from it. The list itself is
; deallocated.
@36dStart with 1 longword needed.
@36dStart at the head node of the list.
@36dGet pointer to the current node.
@36dGet pointer to the next node.
@36dEnd of list reached if zero.
@36d4 longwords needed for this node.
@36dScan the rest of the list.
@36dMEMF_PUBLIC | MEMF_CLEAR.
@36dAllocate memory.
@36dGet pointer to allocated memory.
@36dSave a copy of it.
@36dStart at the head of the RomTag list.
@36dGet pointer to current node.
@36dGet pointer to next node.
@36dEnd of list reached if zero.
@36dStore pointer to this RomTag.
@36d14 bytes.
@36dFreeMem() the list node.
@36dGo process next list node.
@36dMark end of "KickTag" list.
@36dReturn its base address.
@d
; KickTagPtr processor.
; This routine steps through the table(s) of RomTag addresses
; pointed to by the KickTagPtr, and adds all the RomTags pointed
; to by these addresses to the list of found RomTags.
@d
@36dGet the KickTagPtr.
@36dJust exit if it is zero.
@36dPoint to the first tag.
@36dRead a longword from the current tag.
@36dExit if end of list reached.
@36dIf this is a link, handle it.
@36dData must be a pointer to a RomTag.
@36dAdd the RomTag to the list.
@36dContinue with current node.
@36dStrip high bit from pointer to next
@36dnode in the list of "KickTags".
@36dGo process the next node.
@d
@d
---------------------------------------------------------------------------
SumKickData()
---------------------------------------------------------------------------
; This routine computes the KickCheckSum by checksumming all the
; tables associated with the KickMemPtr and KickTagPtr pointers.
; The result should is the KickCheckSum.
@a?fc0a3c
@d
@36dPoint to the KickMemPtr in ExecBase.
@36dGet the KickMemPtr and the KickTagPtr.
@36dClear both to zero in the ExecBase
@36dstructure.
@36dStart checksum at -1.
@36dStart at the old KickMemPtr.
@36dEnd of list reached?
@36dExit from loop if so.
@36dPoint to current MemList.
@36dGet pointer to next MemList.
@36dGet the number of entries in the MemList.
@36dDouble it and add 4 more longwords, to
@36dget the size (in longwords) of this list.
@36dChecksum the MemList.
@36dGo process next MemList, if any.
@36dGet the old KickTagPtr.
@36dSkip the following if none.
@36dPoint to the start of the list.
@36dStart checksumming the list.
@36dAdd a longword to the checksum.
@36dGet first data word at this node.
@36dExit if end of list reached.
@36dIf high bit clear, add the data to the
checksum and go on to the next longword.
@36dClear the high bit.
@36dUse result as pointer to next node.
@36dContinue processing the list.
@36dPut KickMemPtr/KickTagPtr back.
@d
@d
; Subroutine to checksum all the entries in a MemList.
@d
@d
@d
; The KickMemPtr points to a list of MemLists, each pointing to
; some number of chunks of memory. This routine steps through
; all the MemLists, allocating all those chunks. If any can't
; be allocated (memory already grabbed by someone else), it returns
; zero. If successful, it returns 1.
@36dStart where the KickMemPtr points.
@36dEnd of list reached?
@36dIf so, return 1 and exit.
@36dPoint to the current MemList.
@36dGet pointer to the next MemList.
@36dAdvance past the MemList header.
@36dGet number of entries in this MemList.
@36dFake "successful allocation" return code.
@36dEnter the loop.
; Allocate all the pieces of memory indicated by this MemList.
@36dGet the address of this entry.
@36dGet the size of this entry.
@36dAllocAbs()
@36dSuccessful?
@36dIf so and more entries remain, loop.
; When we come out of the loop, if the zero flag is set, we were
; unable to reclaim a piece of memory specified in the MemList.
; In this case, return zero and exit. Otherwise, we have allocated
; them all and go on to the next MemList.
@36dIf failed, go exit.
@36dElse proces next MemList.
@36dIndicate all is OK.
@d
---------------------------------------------------------------------------
resident = FindResident( name )
D0 A1
---------------------------------------------------------------------------
@d
@36dGet pointer to the resident module table.
@d
; Outer loop: Step through the table(s) of RomTags, looking
; for one whose name matches the wanted one.
@36dGet a RomTag address from the table.
@36dReturn zero if end of table reached.
@36dIf table entry is a link then
@38dclear its high bit.
@38duse it as a new table pointer.
@38dGo to the top of the loop.
@36dEndif
@36dGet pointer to desired name.
@36dPoint to RomTag's name field.
; Inner loop: Compare the wanted name with that in the RomTag.
@36dCompare a character.
@36dBack to top of loop if not equal.
@36dDid we reach the terminating zeros?
@36dCompare more characters if not.
; Fall through with the module's address in D0.
@d
@d
---------------------------------------------------------------------------
InitCode( startClass, version )
D0 D1
---------------------------------------------------------------------------
; This function initializes modules from the resident module list.
; The resident module list is one or more tables of RomTag addresses.
; Such tables can be linked together by including the address of the
; next table, with the high bit set, as the last address in a table.
; We are given a set of required flags in D0, and a minimum
; version number in D1. Modules which have flags not set whose bits
; appear in D0, or are of a lower version than D1, will not be
; initialized.
@d
@40dGet the ResModules pointer.
@40dGet forbidden flags.
@40dGet cutoff version number.
@40dRead a module address from the table.
@40dExit if end of table reached.
@40dIs the high bit set? If so, it's
@40da pointer to another table, so strip
@40dthe high bit off, point to the next
@40dtable, and process it.
@40dCheck the module's version number
@40dagainst the cutoff.
@40dIgnore module if too low.
@40dGet the module's flags.
@40dCheck against mask.
@40dIgnore it if required ones not set.
@d
@40dInitResident()
@40dProcess next resident module.
@d
@d
---------------------------------------------------------------------------
InitResident( resident, segList )
A1 D1
---------------------------------------------------------------------------
; This initializes a resident module. The pointer to the module
; is in register A1. If the RT_FLAGS byte at offset 10 from
; the module pointer has the RTF_AUTOINIT flag set, we let the
; module initialize itself by calling its initialization code.
; Otherwise we assume it's a library type of thing, and call
; MakeLibrary with a set of parameters taken from the module.
; If the MakeLibrary succeeds, we add the module to the appropriate
; system list.
@40dCheck the RTF_AUTOINIT flag.
@d
@40dGet the RT_INIT address.
@d
@40dGet the segList pointer.
@40dCall the module's init code.
@d
; The RTF_AUTOINIT flag was not set, so we MakeLibrary() the
; module instead. Note that the address at RT_INIT points to
; a data structure containing parameters for MakeLibrary().
@d
@40dGet the initialization address.
@40dGet parameters for MakeLibrary.
@40dMakeLibrary()
@d
@40dStore the library pointer.
@40dIf zero, just return it and exit.
; The library has been created successfully.
@40dGet the library pointer.
@40dGet the module's node type.
@40dIf the node type is NT_DEVICE then
@d
@42dAddDevice().
@d
@40dElse if it is NT_LIBRARY then
@d
@42dAddLibrary().
@d
@40dElse if it is NT_RESOURCE then
@d
@42dAddResource().
@40dEndif.
@d
; InitStruct entry point to copy one byte several times (-186).
@40dGet the byte initially.
@40dCopy it the required number of times.
@d
@40dBack to main loop.
; Entry ponint for invalid InitStruct commands (-176). Guru time.
@40dSave registers.
@40dAlert number (fatal).
@40dGet ExecBase
@40dPut up the Alert.
@40dThis never gets executed.
; InitStruct entry point to copy one longword several times (-154).
@40dRound A1 up to next even address.
@d
@d
@d
@40dGet the longword initially.
@40dCopy it the required number of times.
@d
@40dBack to main loop.
; InitStruct entry point to copy one word several times (-134).
@40dRound A1 up to next even address.
@d
@d
@d
@40dGet the word initially.
@40dCopy it the required number of times.
@d
@40dBack to main loop.
---------------------------------------------------------------------------
InitStruct( initTable, memory, size )
A1 A2 D0
---------------------------------------------------------------------------
@36dGet structure base address.
@36dDivide size by 2 (get size in words)
@d
@36dLoop: Clear the structure's memory.
@d
@36dGet structure base address again.
@d
@36dRead a byte from the initTable.
@36dIf it is zero, we're done.
@36dCheck and reset offset flag.
@36dBranch if it was not set.
; Process an offset command (10ssnnnn or 11ssnnnn).
@36dCheck and reset byte/rptr flag.
@36dBranch if it was not set.
@36dBack up over command byte.
@36dGet cmd byte + 24 bit data.
@36dMask out the command byte.
@d
; Process an offset/byte command (10ssnnnn).
@d
@36dGet next byte from table into D1.
; Continue here for both offset commands. In each case, A1 now
; points at the next even address in the initTable, and D1 contains
; the offset we have just fetched from it, as a longword.
@36dGet structure base address.
@36dAdd offset just computed.
; Continue here for all commands, A0 now holds the absolute
; address where the next operand should go, D0 contains what is
; left of the command byte, and A1 points at the current even
; address in the table.
@38dGet the command byte
@38dShift and mask it so it becomes
@38d2 * (sdd)
@38dGet the jump offset from the table.
@38dMask all but (nnnn) out of the command.
@38dJump to the right routine.
; Entry point for copying multiple bytes (-42).
@36dCopy one byte.
@36dLoop until required number done.
@36dRound A1 up to next even address.
@d
@d
@d
@36dGo back to main loop.
; Entry point for copying multiple longwords (-24).
; Simply falls through to word copy routine, set up to copy twice as
; many words. An extra 1 must be added to compensate for the effects
; of the "dbra" instruction.
@36dD0 = (2 * D0) + 1.
@d
; Entry point for copying multiple words (-20).
@36dRound A1 up to next even address.
@d
@d
@d
@36dMove the data.
@d
@36dGo back to top of main loop.
@d
; Dispatch table for InitStruct.
@8p@10w-24 (sdd = 000) longword, count.
@8p@10w-20 (sdd = 001) word, count.
@8p@10w-42 (sdd = 010) byte, count.
@8p@10w-176 (sdd = 011) Invalid.
@8p@10w-154 (sdd = 100) longword, repeat.
@8p@10w-134 (sdd = 101) word, repeat.
@8p@10w-186 (sdd = 110) byte, repeat.
@8p@10w-176 (sdd = 111) Invalid.
@8p@28w Padding.
; The following is used to bail out of an interrupt which we should
; have ignored.
@d
@d
; Level 1 Autovector interrupt entry point.
; -----------------------------------------
@d
@36dPoint to custom chip register area.
@36dGet ExecBase.
@36dGet interrupt enable register.
@36dCheck master interrupt enable.
@36dBail out if no interrupts enabled.
@36dCheck for pending & enabled interrupts.
@36dSerial port transmit interrupt?
@d
@36dGet IntVects[0] handler data.
@36dPush address of ExitIntr()
@d
@36dDisk block finished interrupt?
@d
@36dGet IntVects[1] handler data.
@36dPush address of ExitIntr()
@d
@36dSoftware generated interrupt?
@d
@36dGet IntVects[2] handler data.
@36dPush address of ExitIntr()
@d
@36dBail out if nothing found.
; Level 2 Autovector interrupt entry point.
; -----------------------------------------
@d
@36dSee level 1 for comments.
@d
@d
@d
@d
@d
@36dI/O port or timer interrupt?
@d
@36dGet IntVects[3] handler data.
@36dPush address of ExitIntr()
@d
@36dBail out if nothing found.
; Level 3 Autovector interrupt entry point.
; -----------------------------------------
@a?fc0cd8
@d
@36dSee level 1 for comments.
@d
@d
@d
@d
@d
@36dBlitter finished?
@d
@36dGet IntVects[6] handler data.
@36dPush address of ExitIntr()
@d
@36dStart of vertical blank?
@d
@36dGet IntVects[5] handler data.
@36dPush address of ExitIntr()
@d
@36dCopper interrupt?
@d
@36dGet IntVects[4] handler data.
@36dPush address of ExitIntr()
@d
@36dBail out if nothing found.
; Level 4 Autovector interrupt entry point.
; -----------------------------------------
@d
@36dSee level 1 for comments.
@d
@d
@d
@d
@d
@36dAudio channel 1?
@d
@36dGet IntVects[8] handler data.
@36dUse special ExitIntr() below.
@d
@a?fc0d62
@36dAudio channel 3?
@d
@36dGet IntVects[10] handler data.
@36dUse special ExitIntr() below.
@d
@36dAudio channel 0?
@d
@36dGet IntVects[7] handler data.
@36dUse special ExitIntr() below.
@d
@36dAudio channel 2?
@d
@36dGet IntVects[9] handler data.
@36dUse special ExitIntr() below.
@d
@36dBail out if nothing found.
; This routine allows a single invocation of the level 4 interrupt
; handler to service all level 4 interrupts which are pending or
; become pending while one is serviced.
@36dPoint at custom chip register area.
@36dGet ExecBase.
@36dMask for all level 4 interrupt bits.
@d
@36dFind enabled and pending level 4 ints.
@36dIf any still pending, go service them.
@36dOtherwise, do ExitIntr()
; Level 5 Autovector interrupt entry point.
; -----------------------------------------
@d
@36dSee level 1 for comments.
@d
@d
@d
@d
@d
@36dDisk sync interrupt?
@d
@36dGet IntVects[12] handler data.
@36dPush address of ExitIntr()
@d
@36dSerial port receive interrupt?
@d
@36dGet IntVects[11] handler data.
@36dPush address of ExitIntr()
@d
@36dBail out if nothing found.
; Level 6 Autovector interrupt entry point.
; -----------------------------------------
@d
@36dSee level 1 for comments.
@d
@d
@d
@d
@d
@36dSpecial copper interrupt?
@d
@36dGet IntVects[14] handler data.
@36dPush address of ExitIntr()
@d
@36dExternal level 6 interrupt?
@d
@36dGet IntVects[13] handler data.
@36dPush address of ExitIntr()
@d
@36dBail out if nothing found.
; Level 7 Autovector interrupt entry point.
; -----------------------------------------
@d
@d
@36d Get IntVects[15] handler data.
@d
@d
@d
---------------------------------------------------------------------------
ExitIntr()
---------------------------------------------------------------------------
; This routine is called after an interrupt handler has finished.
; It checks if a task switch is necessary. If not, it returns
; from the interrupt. Otherwise, it drops into the scheduler.
; The initial check for supervisor mode is very important. If
; the CPU was in supervisor mode, then there either was no task
; running (CPU stopped), or the task had already been interrupted
; by a lower priority interrupt. In either case, we may not do
; a task switch. In the former case, it will be done after the
; CPU comes out of the STOP instruction in the dispatcher, and
; in the latter, it will be taken care of by the lowest priority
; interrupt handler (which interrupted the CPU while it was not
; in supervisor mode).
@38dCheck if CPU was in supervisor mode.
@38dIf so, just return from interrupt.
@38dGet ExecBase.
@38dSee if task switching is disabled.
@38dIf so, just return from interrupt.
@38dCheck the scheduling attention flag.
@38dReturn from interrupt if not set.
@a?fc0e7a
FC0E7A move.w #$2000,SR Enable all interrupts.
@a=fc0e7e
@d
@d
@d
---------------------------------------------------------------------------
Schedule()
---------------------------------------------------------------------------
; This is the scheduler. It is called with the current task's
; registers saved on the system stack, as put there by the interrupt
; entry point, and PC/SR as put there by the interrupt itself.
;
; The scheduler checks if the task should be suspended and another
; task run in its place. The following decision is made:
;
; IF the current task has an exception pending THEN
; reschedule the current task, so that it will be redispatched,
; so that the dispatcher can process the exception.
; ELSEIF the TaskReady queue is empty THEN
; the current task is the only runnable task in the system, so
; let it continue running.
; ELSEIF the TaskReady queue contains a higher priority task THEN
; do a task switch.
; ELSEIF the current task's time slice has expired THEN
; reschedule the current task (it may be redispatched right away,
; with a new time slice).
; ELSE
; let the current task keep running.
; ENDIF
@d
@a?fc0e8a
FC0E8A move.w #$2700,SR Mask all maskable interrupts.
@a=fc0e8e
@38dReset the scheduling attention flag.
@38dGet the current task pointer.
@38dCheck the task's TB_EXCEPT flag.
@d
@38dPoint to the TaskReady queue.
@38dCheck if the queue is empty.
@38dIf so, return from interrupt.
@38dGet the queue's head node.
@38dGet the head node's priority.
@38dCompare to the current task's priority.
@38dSwitch tasks if higher priority.
@38dCheck the time slice expired flag.
@38dIf not expired, return from interrupt.
; If we get this far, it is necessary to put the current task on
; the TaskReady queue and then run the one at the head of the queue.
; This may be the same one if a TB_EXCEPT is being processed.
@38dPoint to the TaskReady queue.
@38dEnqueue the current task.
@38dMake the task's state TS_READY.
@a?fc0ecc
FC0ECC move.w #$2000,SR Enable all interrupts.
@a=fc0ed0
; Get the current task's saved registers back. They may have been
; put on the stack when an interrupt occurred, or at the start of
; this routine.
@d
@38dInsert some space in the system stack.
@38dPut in the address of Switch().
@38dGet A6 from the stack.
@38dGo to Switch().
---------------------------------------------------------------------------
Switch()
[Version for machines without a numeric coprocessor]
---------------------------------------------------------------------------
; The ExecBase "Switch()" vector points here if no FPP is installed.
; This routine does a task switch. It stores the context of
; the currently running task, then drops into the dispatcher.
; The routine stores the current CPU context on the user
; stack. The status register and program counter are popped from
; the supervisor stack. The current interrupt disable nesting
; level and the user stack pointer are then saved in this task's
; task descriptor.
; If the TB_SWITCH bit is set in the task descriptor, the task's
; TC_SWITCH function is called.
@a?fc0ee0
FC0EE0 move #$2000,SR Enable all interrupts.
@a=fc0ee4
@40dStack A5 on the supervisor stack.
@40dGet the user stack pointer.
@40dStack all registers on user stack.
@40dGet ExecBase
@40dGet current interrupt disable level.
@40dSet interrupt disable level to -1.
@40dEnable interrupts.
@40dFix A5 in the saved register set.
@40dSave the status register.
@40dSave the program counter.
@40dAddress of context-restore routine
for machines without an FPP.
@40dFind the current task descriptor.
@40dStore interrupt disable level.
@40dStore user stack pointer.
@40dCheck the TB_SWITCH bit
@d
@40dIf TB_SWITCH bit was set, then
@40dcall this task's TC_SWITCH function.
@d
---------------------------------------------------------------------------
Dispatch()
[Version for machines without a numeric coprocessor]
---------------------------------------------------------------------------
; This routine dispatches the next runnable task, if there is one.
; If the TaskReady queue is empty, the routine waits, stopping
; the processor and checking the queue again each time it resumes
; running (after an interrupt).
; When a runnable task is found, it is dispatched. Its context
; and interrupt disable level are restored, and the TB_LAUNCH
; and TB_EXCEPT flags are checked. If either is set, they are
; handled.
@40dAddress of context-restore routine
for machines without an FPP.
@40dInitialize interrupt disable level
@40dat -1, and enable interrupts.
@40dPoint at the TaskReady queue.
@a?fc0f40
FC0F40 move #$2700,SR Mask all maskable interrupts.
@a=fc0f44
@40dCheck for a task descriptor at
@40dthe front of the queue.
@d
; TaskReady queue is empty. Halt the processor until the next
; interrupt, then check for runnable tasks again.
@40dIncrement the idle counter.
@40dSet the rescheduling attention flag.
@a?fc0f54
FC0F54 stop #$2000 Enable all interrupts and stop.
@a=fc0f56
@d
@40dGo back and check for runnable tasks.
; We have a runnable task.
@40dUnlink the task descriptor from
@40dthe TaskReady queue.
@d
@40dIncrement the dispatch counter.
@40dSet the current task pointer.
@40dInitialize the time-slice counter.
@40dReset the time slice expired flag.
@40dSet the task's state to TS_RUN.
@40dRestore the interrupt disable level.
@d
@40dDisable interrupts if interrupt
@40ddisable level >= 0.
@a?fc0f90
FC0F90 move #$2000,SR Enable all interrupts.
@a=fc0f94
@40dGet the task's flags.
@40dCheck for TB_EXCEPT or TB_LAUNCH.
@d
@40dProcess the flags if either was set.
@40dGet the user stack pointer.
@40dRestore the CPU context.
; Context Restore routine (non-FPP version)
; -----------------------------------------
; Pop the task's CPU context off the user stack, and start it
; running. The program counter and status register are set by
; storing them on the supervisor stack for the "RTE" instruction.
@40dGet the user stack pointer.
@40dSet it.
@40dGet the program counter.
@40dGet the status register.
@40dRestore all the other registers.
@40dAnd start running the task.
; Subroutine to handle TB_LAUNCH and TB_EXCEPT.
@40dCheck the TB_LAUNCH bit.
@d
@40dIf it was set, call the task's
@40dTC_LAUNCH routine.
@d
@40dRestore D0.
@40dCheck the TB_EXCEPT bit.
@d
@40dReturn if it was clear.
---------------------------------------------------------------------------
Exception()
---------------------------------------------------------------------------
; This routine handles task-level exception processing.
; It checks whether any signals have occurred which should cause
; a software exception. If so, it calls the task's exception
; processing routine. After this returns, it restores everything
; so that the task itself can be dispatched.
@40dReset the TB_EXCEPT bit.
@40dDisable()
@d
@40dGet received signals.
@40dCompare with exception signals.
@40dIf any exception signals were set,
@40dreset them in both places.
@40dEnable()
@d
@d
@40dGet the user stack pointer.
; Store tc_Flags, tc_State, tc_IDNestCnt, tc_TDNestCnt on user stack.
@d
; If interrupts have been disabled exactly once, then enable them.
@d
@d
@d
@d
@d
; Put the address of an exception post-processing routine on the
; user stack. This gets executed when the exception routine
; does an RTS.
@40dPut return address on user stack.
@40dSet user stack pointer.
@40dSee if this is a plain 68000.
@d
@40dMake 68010/020 extended stack frame.
@40dPut exception code and a fake status
@40dword on the supervisor stack.
@40dPoint to the exception handler's
data segment.
@d
; Continue here after the user's exception handling routine has run.
@40dGet ExecBase.
@40dWhere to go in supervisor mode.
@40dGo to supervisor mode.
@40dAddress of context restore routine.
@40dIs this a 68010/020?
@d
@40dIf so, handle extended stack frame.
@40dDo we have a 688881?
@d
@40dIf so, use the other context-restore.
@40dPop PC and SR from stack.
@40dGet current task descriptor.
@40dSet the exception signals up again.
@40dGet the user stack pointer.
; Restore tc_Flags, tc_State, tc_IDNestCnt, tc_TDNestCnt from
; user stack.
@d
@40dSet USP image in task descriptor.
@40dRestore interrupt disable level.
@d
@d
@40dDisable interrupts if needed.
@d
---------------------------------------------------------------------------
Switch()
[Version for machines with a 68881 FPP]
---------------------------------------------------------------------------
@a?fc108a
FC108A move #$2000,SR Enable all interrupts.
@a=fc108e
@40dStore A5 on the supervisor stack.
@40dGet the user stack pointer.
@40dSave all registers on user stack.
@40dGet ExecBase.
@40dGet current interrupt disable level.
@40dSet interrupt disable level to -1.
@40dEnable interrupts.
@40dFix A5 in the saved register set.
@40dSave the status register.
@40dSave the program counter.
; 68020/68881 specific material. Without documentation for those
; processors, I can't comment this.
@a?fc10b4
FC10B4 fsave -(A5)
@a=fc10b6
@d
@d
@d
@d
@d
@d
@d
@d
@d
@d
@d
@d
@d
@a?fc10d6
FC10D6 fmovem.x FP0-FP7,-(A5)
FC10DA fmovem.l FPCR/FPSR/FPIAR,-(A5)
@a=fc10de
@d
@40dPoint to 68881 context restore.
; The rest of the processing for Switch() is the same, so enter
; the "plain" version in the middle.
@d
---------------------------------------------------------------------------
Dispatch()
[Version for machines with a 68881 FPP]
---------------------------------------------------------------------------
@40dPoint to 68881 context-restore.
; The regular Dispatch() can be used for the rest.
@d
; Special 68881 FPP compatible context restore
; --------------------------------------------
; 68020/68881 specific material. Without documentation for those
; processors, I can't comment this.
@d
@d
@d
@a?fc10f6
FC10F6 fmovem.l (A5)+,FPCR/FPSR/FPIAR
FC10FA fmovem.x (A5)+,FP0-FP7
@a=fc10fe
@d
@d
@d
@d
@d
@d
@d
@a?fc1110
FC1110 frestore (A5)+
@a=fc1112
@40dThis part is the same as the regular
@40dcontext-restore routine.
@d
@d
@d
@d
---------------------------------------------------------------------------
oldSR = SetSR( newSR, mask)
D0 D0 D1
---------------------------------------------------------------------------
@d
@36dWhat to execute in supervisor mode.
@36dEnter supervisor mode.
@d
@36dNow in supervisor mode. Restore A5.
@36dGet status register from stack.
@36dMask unwanted bits out of newSR.
@d
@36dGet unchanged bits from old status reg.
@36dOr in the bits to be changed.
@d
@36dReturn old status codes in D0.
@36dLoad status reg. and return to user mode.
---------------------------------------------------------------------------
conditions = GetCC()
D0
[Version for 68000 machines only]
---------------------------------------------------------------------------
; The ExecBase vector will have been changed not to point here
; unless the CPU is a 68000, since the following instruction would
; bomb with a privilege violation on 68010/020 processors.
@a?fc1140
FC1140 move SR,D0 Get the status register.
@a=fc1142
@36dClear all but the condition codes.
@d
---------------------------------------------------------------------------
oldSysStack = SuperState()
D0
---------------------------------------------------------------------------
@d
@36dWhat to execute in supervisor mode.
@36dEnter supervisor mode.
@d
@36dContinue here. Restore A5.
@d
@36dSet supervisor bit in status word.
@36dDo nothing more if it was already set.
@a?fc115e
FC115E move (SP)+,SR Get status register from stack.
@a=fc1160
@36dSave supervisor stack pointer.
@36dLoad user stack pointer.
@36dCheck if running on a plain 68000.
@d
@36dPop 68010/020 exception info from stack.
@36dPop exception return address from stack.
@36dReturn to user.
@d
---------------------------------------------------------------------------
UserState( sysStack )
D0
---------------------------------------------------------------------------
@d
@36dWhere to go in supervisor mode.
@36dEnter supervisor mode (just in case).
@36dRestore A5.
@36dGet status register from stack.
@36dCopy stack pointer to user stack pointer.
@36dSet the system stack pointer.
@36dClear the supervisor mode bit.
@a?fc118a
FC118A move D1,SR
@a=fc118c
@36dReturn to the user.
---------------------------------------------------------------------------
oldInterrupt = SetIntVector( intNumber, interrupt )
D0 D0 A1
---------------------------------------------------------------------------
; This routine makes an interrupt node the active interrupt node
; for a given interrupt number. This node corresponds to an
; interrupt handler. There can only be one node associated with
; each interrupt number.
; Note: Never try to SetIntVector() one of the interrupts which are
; dispatched as chains of servers. The handler which does this
; does not use regular interrupt vectors or nodes, and it would not
; be possible to hook it up to the interrupt again if it became
; disconnected.
@36dCompute address of
@36dExecBase->IntVects[intNumber]
@36dDisable()
@d
@36dGet pointer to current interrupt node.
@36dInstall pointer to new interrupt node.
@36dIf there is a new node, then
@36d Install interrupt data pointer.
@36d Install interrupt code pointer.
@36dElse
@36d Set code and data pointers to -1.
@d
@36dEndif
@36dEnable()
@d
@d
@d
---------------------------------------------------------------------------
AddIntServer( intNumber, interrupt )
D0 A1
---------------------------------------------------------------------------
; Adds an interrupt server to the server chain for a given
; interrupt number. The interrupt number must be one of the
; set for which interrupt server lists have been set up.
; The interrupt for which the server is being added will be enabled
; if it is not already enabled.
@d
@36dSave interrupt number.
@d
@36dCompute
@36dExecBase->IntVects[intNumber].is_Node
@d
@36dDisable()
@d
@36dEnqueue the interrupt server.
@36dHardware-enable the interrupt
@36dcorresponding to the server just added.
@d
@36dEnable()
@d
@d
@d
@d
---------------------------------------------------------------------------
RemIntServer( intNumber, interrupt )
D0 A1
---------------------------------------------------------------------------
; This routine takes an interrupt server off the server chain for
; a given interrupt. The interrupt is disabled if this leaves the
; server chain empty.
@d
@36dCompute
@36dExecBase->IntVects[intNumber].is_Node
@d
@36dSave interrupt number and node.
@d
@36dDisable()
@d
@36dUnlink interrupt server from the chain.
@d
@36dCheck if the chain is now empty.
@d
@36dChain is empty. Disable the interrupt
@36dcorresponding to this node.
@d
@36dEnable()
@d
@d
@d
@d
; The following routine is used at system startup to initialize
; the exec's internal interrupt handlers.
; It initializes the server chains used to dispatch servers for
; I/O and timer, Copper, vertical blank, external level 6, and
; non-maskable interrupts, and establishes vectors to the exec's
; built-in interrupt handlers.
; For each server chain, a 22-byte data structure is built,
; consisting of a list header and 4 words of control data. The
; 5 server chain headers are put into a contiguous 110-byte
; section of memory. The server chains are initialized to empty,
; and the rest of the data structure is initialized with the
; data required to assert, deassert, and cancel the interrupt.
@d
@36d110 bytes needed.
@36dAttributes = MEMF_PUBLIC | MEMF_CLEAR.
@36dRequest memory.
@36dSee if we got the memory.
@d
@36dIf not, it's guru time!
@36dUse this alert number.
@36dGet ExecBase.
@36dGo put up the alert.
@36dNever reached.
@36dLoop 5 times (4 + 1 for dbra).
@36dGet pointer to allocated memory.
@36dPoint to the data table below.
; Loop: Divide the 110 byte data area up into five data
; structures, each consisting of a list header and some control
; words.
@36dSave pointer to current node.
@36dInitialize the node's server chain
@36dto empty.
@d
@d
@36dPoint to the node's data area.
@36dGet interrupt number from table.
@36dMake the control word needed to disable
@36dthis interrupt.
@36dWrite it in the node's data area.
@36dMake the control word needed to enable
@36dthis interrupt and store it also.
@36dGet the cancel interrupt control word.
@36dMove a word of pad data.
@36dPoint to the generic interrupt handler.
; Set up this interrupt in the exec table. The interrupt vector
; is at ExecBase + $54 + 12 * interrupt number. We write the code
; and data addresses only; we don't bother with the node address.
@d
@d
@36dLoop until all interrupts done.
; Install the address of the software interrupt handler. The
; software interrupt lists have already been cleared earlier in
; the system startup code.
@d
; Now enable interrupts and exit. Note that only the software
; interrupts are enabled. The interrupts handled by the handler
; below will be enabled if and when servers are put on their
; chains. Interrupts handled external to the exec must be enabled
; by whoever provides the handler.
@d
@d
@d
; Interrupt initialization table. Each line consists of an
; interrupt number, and a control word which is used to cancel
; the interrupt if pending. The last column appears to be padding.
@8p@5w@5w@w
@8p@5w@5w@w
@8p@5w@5w@w
@8p@5w@5w@w
@8p@5w@5w@w
; Generic Interrupt Handler
; -------------------------
; Each logical interrupt (0-15) has a handler. Some logical
; interrupts are accessible to the user not as handlers, but rather,
; as chains of servers. Most of these (all but the Blitter one)
; use this code, which acts as a handler and dispatches the servers.
; A data structure set up by the previous routine exists for each
; interrupt to store the list header for the server chain, and some
; control words.
@d
@d
@35dCheck the head of the server chain.
; Main loop: Here we dispatch a server from the queue.
@35dIs this the end of the chain?
@35dIf so, exit.
@35dGet the server's code and data addresses.
@35dCall it.
; Servers return with the zero flag indicating whether they have
; taken care of the interrupt. We keep calling servers until
; we either run out or until one returns the zero flag clear.
@35dIf the zero flag is clear, exit.
@35dGo to next node in the queue.
@35dGo back to top of loop.
; Either we ran out of servers, or one of them accepted the
; interrupt. Either way, clear the interrupt and exit.
@d
@35dClear the interrupt.
@d
---------------------------------------------------------------------------
Cause( interrupt )
A1
---------------------------------------------------------------------------
@36dDisable()
@d
@36dCheck if node type is already NT_SOFTINT.
@36dIf so, do nothing.
@36dMake node type NT_SOFTINT.
@d
@36dGet node priority.
@36dTruncate to a multiple of 16.
@36dSign extend to a word quantity.
@36dPoint at middle of ExecBase->SoftInts
@36dGet address of SoftInts entry correspon-
ding to this priority (-32,-16,0,16,32).
; Now enqueue the node on the SoftIntList for this priority.
@d
@36dGet old TailPred pointer.
@36dSet TailPred pointer to new node.
@36dSet forward pointer of new node.
@36dSet back pointer of new node.
@d
@36dSet forward pointer of old tail node.
@36dSet the software interrupt pending flag.
@36dGenerate a level 1 "software" interrupt.
@36dEnable()
@d
@d
@d
; Software Interrupt Handler
; --------------------------
; This is the interrupt handler for logical interrupt #2, which
; is used for software interrupts. It will be called as soon after
; the above routine as its interrupt priority will allow.
@36dClear the interrupt.
@36dClear the software int. pending flag.
@36dIf it wasn't set, ignore the interrupt.
@d
@36dDisable further software interrupts.
@36dGo and scan the SoftInts table.
; This code is called (from below) when a non-empty SoftIntList
; has been found. The pointer of the list will be in A0.
@a?fc139c
FC139C move.w #$2700,SR Mask all maskable interrupts.
@a=fc13a0
@36dGet the pointer to the first list node.
@d
@d
@36dUnlink the node from the list.
@d
@d
@d
@36dMake the node type NT_INTERRUPT.
@a?fc13b6
FC13B6 move.w #$2000,SR Enable all interrupts.
@a=fc13ba
@36dGet the interrupt code and data.
@36dCall the node's interrupt handler.
; The following scans the SoftInts[] table for non-empty
; SoftIntLists. If it finds one, it executes the code above.
@36dLoop 5 times (1 less for dbra).
@36dStart at SoftInts[4] (priority 32).
@36dClear the software interrupt.
@36dSee if there are any nodes on this list.
@36dIf so, process the list.
@36dOtherwise, go to next lower priority,
@36dand loop back.
; No (more) nodes found.
@a?fc13de
FC13DE move #$2100,SR
@a=fc13e2
@36dReenable software interrupts and exit.
@d
---------------------------------------------------------------------------
Disable()
---------------------------------------------------------------------------
; Hardware disable all interrupts, and log the fact that this
; was done in the interrupt disable nesting level counter.
@d
@d
@d
---------------------------------------------------------------------------
Enable()
---------------------------------------------------------------------------
; Decrement the interrupt level nesting counter, and if it goes
; negative, enable interrupts again.
@d
@d
@d
@d
@8p@28wPadding.
---------------------------------------------------------------------------
AddLibrary( library )
A1
---------------------------------------------------------------------------
@36dPoint to system library list.
@36dAdd the library to the list.
@36dUpdate the library vector checksum.
@d
---------------------------------------------------------------------------
error = RemLibrary( library )
D0 A1
---------------------------------------------------------------------------
; NOTE: Some other part of the system, presumably DOS, will patch
; itself in ahead of this.
@d
@36dForbid()
@36dSave ExecBase.
@d
@36dCall the library's Expunge() function.
@36dRestore ExecBase.
@36dPermit()
@d
---------------------------------------------------------------------------
library = OldOpenLibrary( libName )
D0 A1
---------------------------------------------------------------------------
; This is just OpenLibrary() without the "version" parameter.
@36dAny version of the library will do.
@36dOpenLibrary()
@d
---------------------------------------------------------------------------
library = OpenLibrary( libName, version )
D0 A1 D0
---------------------------------------------------------------------------
; NOTE: Some other part of the system, presumably DOS, will patch
; itself in ahead of this, presumably so it can pull libraries off
; disk if they aren't already in the library list.
; Note how several versions of a library can be in the system
; library list (although the initial RomTag system of putting them
; there will only take the newest one), and this routine will keep
; looking for one new enough to satisfy the request if it finds
; one which is too old.
@d
@d
@36dForbid()
@36dPoint to the system library list.
@36dFindName()
@36dDid we find the library?
@36dReturn zero and exit if not.
@d
@36dCheck the version number.
@36dTry to find another one if too old.
; We have an acceptable library.
@36dSave ExecBase.
@36dPoint A6 at the library base address.
@36dCall the library's Open() function.
@36dRestore ExecBase.
@36dPermit()
@d
@d
---------------------------------------------------------------------------
CloseLibrary( library )
A1
---------------------------------------------------------------------------
; NOTE: Some other part of the system, presumably DOS, will patch
; itself in ahead of this.
@36dForbid()
@36dSave ExecBase.
@d
@36dCall the library's Close() function.
@36dRestore ExecBase.
@36dPermit()
@d
---------------------------------------------------------------------------
oldFunc = SetFunction( library, funcOffset, funcEntry )
D0 A1 A0.W D0
---------------------------------------------------------------------------
@36dIndicate that library is modified.
@36dCompute address of jump instruction.
@36dGet the old jump destination address.
@36dInstall a "JMP" instruction.
@36dInstall the new destination address.
@36dRe-checksum the library.
@36dReturn the previous jump destination.
@d
---------------------------------------------------------------------------
SumLibrary( library )
A1
---------------------------------------------------------------------------
@36dCheck the "sum used" flag.
@36dIf sum not used, then don't bother.
@d
@36dClear the "changed" flag.
@d
@36dIf the flag was set, clear the checksum.
@d
@36dGet the negative size.
@36dDivide by 2 (gives size in words).
@36dStart the sum at zero.
@d
; Loop: Add up all the 16-bit words in the library's jump vector.
@d
@d
@36dGet the old checksum.
@d
@36dOld checksum wasn't zero, so compare
@36dit to the new checksum.
; Checksums didn't match, so put up an alert.
@d
@36dLibrary checksum alert number.
@d
@36dCall the alert function.
@d
@36dStore the new checksum in the library.
@36dEnable multitasking.
@d
---------------------------------------------------------------------------
library = MakeLibrary( vectors, structure, init, dataSize, segList )
D0 A0 A1 A2 D0 D1
---------------------------------------------------------------------------
@d
@36dSize of library data area.
@36dPointer to library vector table.
@36dPointer to initialization structure.
@36dFunction to call after creating library.
@36dSegList (for DOS use).
@36dGet pointer to vector table.
@36dSkip the following if no vectors.
@d
@d
@36dCheck if vector data is absolute or
@36drelative (first word = -1).
@d
@36dRelative vectors. Skip over the flag.
@36dCount the vectors up to the terminating
@36dflag. D3 holds -(number of vectors + 1)
@d
@36dAbsolute vectors. Count as above.
@d
@36dMake number of vectors positive.
@36dMultiply by 6.
@d
@36dAdd size of data area.
@36dMEMF_PUBLIC | MEMF_CLEAR
@36dRequest memory for library node.
@36dSee if the memory was allocated.
@d
@36dDidn't get any memory. Fail by returning
@36da nil pointer to the caller.
@36dPoint to start of allocated memory.
@36dSkip over jump vector area.
@36dInstall negative size in library node.
@36dInstall positive size.
@d
@36dClear A2.
@36dPoint at vector table again.
@36dCheck if vectors are relative.
@d
@36dIf vectors are relative, skip over flag,
@36dand set A2 to vector table address.
@36dInstall the jump vector.
@36dCheck if there is an initialization
@36dstructure. Skip the following if not.
@d
@d
@36dDo not clear memory before initializing.
@36dProcess the initialization structure.
@36dGet pointer to library node.
@36dCheck if there is initialization code.
@36dSkip the following if not.
@36dGet the address of the code.
@36dGet the address of the segList
@36dCall the initialization code.
@d
@d
---------------------------------------------------------------------------
size = MakeFunctions( vectors, offset )
D0 A1 A2
---------------------------------------------------------------------------
@d
@36dClear size counter.
@36dCheck if vector table is relative.
@d
; Process a relative vector initialization table.
@36dGet a relative table entry.
@36dCheck if end of table marker.
@d
@36dCompute absolute vector address.
@36dInstall in jump vector.
@36dInstall "JMP" instruction.
@36dIncrement size counter.
@d
; Process an absolute vector initialization table.
@36dGet an absolute table entry.
@36dCheck if end of table marker.
@d
@36dInstall it in the jump table.
@36dInstall "JMP" instruction.
@36dIncrement size counter.
@d
@36dDone, return to caller.
@d
---------------------------------------------------------------------------
Insert( list, node, listNode )
A0 A1 A2
---------------------------------------------------------------------------
@32dCheck if node to insert after is zero.
@32dIf so, do an AddHead instead.
@32dSee if list node indicates end of list.
@32dBranch if so.
; If we get this far, A2 must point to a list node.
@d
@32dSet forward and back pointers of new node.
@32dSet back pointer of next node.
@32dSet forward pointer of previous node.
@d
; The "Successor" field of the listNode was zero, i.e. listNode
; is really the end of list marker. In this case, insert just
; before listNode, i.e. at the end of the list (undocumented).
@32dSet forward pointer of new node to end.
@32dGet old TailPred pointer
@32dLink previous end node to new node.
@32dSet the TailPred pointer to the new node.
@32dLink this node to the previous end node.
@d
---------------------------------------------------------------------------
AddHead( list, node )
A0 A1
---------------------------------------------------------------------------
@32dGet old pointer to first node.
@32dMake the first node the new node.
@32dSet forward and back pointers in new node.
@d
@32dFix back pointer in previous first node.
@d
---------------------------------------------------------------------------
AddTail( list, node )
A0 A1
---------------------------------------------------------------------------
@32dPoint A0 to list's "Tail" field.
@32dSave old value of TailPred field.
@32dMake the new node the last node.
@32dSet new node's back pointer to list header.
@32dSet new node's back pointer.
@d
@32dLink new node to previous last node.
@d
---------------------------------------------------------------------------
Remove( node )
A1
---------------------------------------------------------------------------
@32dGet pointer to successor node.
@32dGet pointer to predecessor node.
@32dPoint predecessor to successor.
@32dPoint successor to predecessor.
@d
---------------------------------------------------------------------------
node = RemHead( list )
D0 A0
---------------------------------------------------------------------------
@32dGet list's head pointer.
@32dGet first node's successor.
@32dIf zero, list is empty, so return zero.
@32dPoint head pointer past removed node.
@d
@32dPoint new head node back at list header.
@d
---------------------------------------------------------------------------
node = RemTail( list )
D0 A0
---------------------------------------------------------------------------
@a?fc161e
@32dGet list's TailPred pointer.
@32dCheck if the last node has a predecessor.
@32dIf not, the list is empty, so return zero.
@32dPoint TailPred pointer at second-last node.
@d
@32dPoint second-last node at the list header.
@32dIncrement to point at "Tail" field.
@d
---------------------------------------------------------------------------
Enqueue( list, node )
A0 A1
---------------------------------------------------------------------------
@32dGet the priority field of the node.
@32dGet the head pointer of the queue.
; Loop: Keep skipping nodes until the end of the list is
; reached or the next node has a lower priority.
@32dPoint at the current node.
@32dCheck if there is a successor.
@32dIf not, we are at the end of the list.
@32dCompare priorities.
@32dLoop until priority is lower than new node's.
; A0 now points at the node to insert before, or at the end
; of the list (i.e. the list header plus 4).
@32dGet old value of back pointer
(or TailPred pointer of list header).
@32dChange it to point to the new node.
@32dSet new node's forward pointer.
@32dSet new node's back pointer to last node
skipped, or list header.
@d
@32dSet list's head field, or forward pointer
of previous node, to new node.
@d
---------------------------------------------------------------------------
node = FindName( start, name )
D0 A0 A1
---------------------------------------------------------------------------
@d
@36dPoint to list header or first node.
@36dSave address of match string in D1.
@d
@36dExit if list is empty.
; Main loop: Check if node pointed to by D0 has the same name
; as the string pointed to by D1.
@36dGet the address of the node to check.
@d
@36dExit if this is the end of the list.
@36dPoint A0 at name of this node.
@36dPoint A1 at match string.
; Inner loop: Compare the strings.
@36dCompare one character.
@36dSkip to next node if not equal.
@36dWas this the terminating zero?
@36dContinue comparing if not.
@36dGot a match - point D0 to the node.
@36dRestore registers and exit.
@d
@d
; Protected enqueue and dequeue routines
; --------------------------------------
@36dForbid()
@36dEnqueue()
@36dPermit()
@d
@36dForbid()
@36dRemove()
@36dPermit()
@d
---------------------------------------------------------------------------
memoryBlock = Allocate( freeList, byteSize )
D0 A0 D0
---------------------------------------------------------------------------
@36dZero bytes requested?
@36dIf so, just return.
@d
@36dRound D0 up to nearest multiple of 8.
@d
@d
@36dCompare D0 to total number of bytes free.
@36dIf greater, return zero.
@36dPoint at the link to the first MemChunk.
; Loop: Scan the free list for a MemChunk big enough to satisfy
; the request.
@36dGet the pointer to the next MemChunk.
@36dIf no more MemChunks, return zero.
@d
@36dCheck D0 against size of this chunk.
@36dIf smaller or equal, take it.
@36dOtherwise, go to next MemChunk.
@36dContinue searching.
; This is where memory fragmentation occurs. If the block we've
; found is bigger than what we need, we take what we need and make
; a smaller block out of the rest.
@36dIf sizes are not the same, then
@36d Compute address of leftover block.
@36d Move the MemChunk pointer up.
@36d Get the current size.
@36d Subtract the allocated amount.
@36d Put it into the new MemChunk.
@36d Link to previous MemChunk (or header).
@36dElse
@36d Unlink this MemChunk.
Endif
@36dDecrease free memory value in header.
@36dGet address of allocated block.
@36dReturn zero or address of block.
@d
@d
; Routine to put up a "Corrupt Free List" dead-end alert.
@a?fc16ee
@d
@36dAlert number.
@36dGet ExecBase.
@36dPut up the alert.
@36dNever reached.
---------------------------------------------------------------------------
Deallocate( freeList, memoryBlock, byteSize )
A0 A1 D0
---------------------------------------------------------------------------
@36dSee if byte size is zero.
@36dDo nothing if so.
@d
@d
@36dTruncate block address to nearest
@36dmultiple of 8.
@36dFind out by how much that decreased
@36dthe address, then add this amount to
@36dthe number of bytes to free.
@36dNow round number of bytes to free up to
@36dnearest multiple of 8.
@36dExit if the result is zero bytes.
; Block address and size are now nice multiples of 8, so we can
; go ahead and put the block into the free list.
@36dPoint at the link to the first MemChunk.
@36dGet the address of the first MemChunk.
@36dBranch if there are no MemChunks.
; Main loop: Find the first MemChunk with an address greater than
; that of our memory block.
@36dCompare addresses.
@36dExit loop if right MemChunk found.
@36dIf addresses are equal, guru time!
@d
@36dGet address of next MemChunk.
@36dContinue searching.
; A2 now points at the link which comes directly before the position
; where our block must be inserted. First, we check whether this
; is the start of the list. If so, we just link it in.
@36dCompute address of MemHeader's link.
@d
@36dSee if A2 still points there.
@36dIf so, insert block at head of the list.
; We are somewhere other than at the start of the list. It is
; possible that we can join the block to the previous MemChunk.
; Try this now.
@36dCompute first address after the
@36dprevious MemChunk.
@36dCompare to address of our new block.
@36dIf equal, join the two together.
@36dIf greater, free list is corrupt.
; Enter here to make a new MemChunk and link it into the free list.
@36dSet new MemChunk's link field.
@36dPoint previous link at the MemChunk.
@36dPut the block size into the MemChunk.
@d
; Enter here to attach the new block to the previous MemChunk.
@36dAdd to previous MemChunk's size.
@36dBack pointer up to that MemChunk.
; Now the block is taken care of. Either we have built a MemChunk
; for it, or we have attached it to a MemChunk directly before the
; block. Either way, the address of the MemChunk where the block
; now is is in A1. It remains to check whether we can join this
; MemChunk with the next one down the line.
@36dSee if there is a next MemChunk.
@36dExit if not.
@36dGet our MemChunk's size.
@36dAdd to its base address.
@36dCompare to address of next MemChunk.
@36dIf higher, free list is corrupt.
@36dIf not equal, we can't join them.
; If we get this far, we can join this MemChunk to the next one down
; the line, so we delete the next one and add its size to this one.
@36dGet pointer to next MemChunk.
@36dFetch its link and put in our MemChunk.
@36dGet its size.
@36dAdd to size of our MemChunk.
; This is where all the paths meet again.
@36dAdd size of freed block to free counter.
@d
@d
; Error exit routine. Generate a dead-end alert.
@d
@36dAlert number.
@36dGet ExecBase.
@36dGo put up the alert.
@36dNever reached.
---------------------------------------------------------------------------
memoryBlock = AllocMem( byteSize, requirements )
D0 D0 D1
---------------------------------------------------------------------------
; NOTE: Some other part of the system, presumably DOS, will patch
; itself in ahead of this, presumably so it can throw out unused
; libraries, devices, fonts, etc. when memory runs low.
@36dForbid()
@d
@d
@d
@36dPoint to the system free memory list.
; Loop: Step through the system free memory list looking for
; a MemHeader which can satisfy the memory request.
@36dGet pointer to a MemHeader.
@36dEnd of memory list reached?
@d
@36dIf so, return a null pointer and exit.
@d
@36dGet the MemHeader's attributes.
@36dCheck against needed attributes.
@d
@36dGo to next MemHeader if not present.
; The current MemHeader has the right attribute flags, now try
; to get a block of the required size out of its free list.
@36dPoint to the MemHeader.
@36dIndicate size of block required.
@36dAllocate()
@36dDid we get the memory?
@36dIf not, go to next MemHeader.
; We got a memory block. Clear it if necessary.
@36dWas the MEMF_CLEAR flag set?
@36dJust return if not.
@d
@36dConvert number of bytes to nearest number
@36dof longwords.
@36dPoint to the memory block.
@36dEnter the loop at the bottom.
; Loop: Clear the memory block.
@36dClear one longword.
@36dLoop back until none remain.
@36dDBRA instruction only uses low 16 bits
@36das program counter, so if any remain
@36dset in the upper half of D3, decrement
@36dthem manually and loop back.
@d
@d
@36dPermit()
@d
@d
---------------------------------------------------------------------------
FreeMem( memoryBlock, byteSize )
A1 D0
---------------------------------------------------------------------------
@36dForbid()
@36dIs number of bytes to free zero?
@36dIf so, just exit.
@36dPoint to system free memory list.
; Loop: Look for the right MemHeader to put the block back into.
@36dPoint to the next MemHeader.
@36dEnd of list reached?
; If no MemHeader can be found to put this block on, something
; is wrong, so it's guru time.
@36dGuru time if end of list reached.
; See if the block falls into the address range covered by this
; MemHeader.
@36dDoes block start before mh_Lower?
@36dIf so, try next MemHeader.
@36dDoes block start after mh_Upper?
@36dIf so, try next MemHeader.
; The block belongs to this MemHeader, so go link it back into
; the free list.
@36dDeallocate()
@36dPermit()
@d
---------------------------------------------------------------------------
attributes = TypeOfMem( address )
D0 A1
---------------------------------------------------------------------------
@36dForbid()
@36dPoint to system free memory list.
@d
@36dPoint to next MemHeader.
@36dEnd of list reached?
@36dIf so, return zero and exit.
@36dCheck if the block is in the address
@36drange covered by this MemHeader.
@d
@36dIf it is, return the contents of the
@36dMemHeader's attribute field.
@36dPermit()
@d
---------------------------------------------------------------------------
memoryBlock = AllocAbs( byteSize, location )
D0 D0 A1
---------------------------------------------------------------------------
@36dForbid()
@d
@36dGet the start address.
@36dTruncate down to nearest multiple of 8.
@d
@36dAdd any difference to number of bytes
@36dwanted, then round number of bytes
@36dup to nearest multiple of 8.
@36dPoint to the system free memory list.
; Loop: Scan through all the MemHeaders in the free list.
@36dPoint to next MemHeader.
@36dEnd of list reached?
@36dIf so, allocation failed, return zero.
; Check whether the block we want is covered by this MemHeader.
@36dIf block starts before this MemHeader's
@36darea, go on to the next one.
@36dIf block starts after this MemHeader's
@36darea, go on to next one.
; The desired block starts in the address range covered by this
; MemHeader, so it's either this one or nothing.
; Quick check: If the total number of bytes free in this MemHeader's
; free list is less than the size of the block wanted, then the
; allocation is going to fail anyway.
@37dCheck block size against mh_Free.
@37dAllocation failed if higher.
@37dGet start address of block wanted.
@d
@37dCompute end address of block wanted.
@37dPoint to link to first MemChunk.
; Loop: Find the MemChunk in the free list which contains the
; wanted block.
@37dGet pointer to next MemChunk.
@37dAllocation failed if no more MemChunks.
@d
@37dGet the MemChunk's size.
@37dCompute its end address.
@37dCheck against needed end address.
@37dExit loop if equal or higher.
@d
@37dGo to the next MemChunk.
; We have a MemChunk whose end address is greater than or equal
; to the end address of the block we want. It's either this
; MemChunk or none at all.
@37dCheck start addresses.
@37dAllocation failed if needed block
starts before this MemChunk.
; The block we want is entirely within this MemChunk, so now we
; cut it out and make zero, one or two MemChunks out of what's left.
@37dRemove block size from free count.
@37dCompare end addresses.
@d
; The end addresses of the allocated block and the MemChunk it
; was in were exactly equal.
@d
@d
; Memory was left over between the allocated block and the end of
; the MemChunk. Make a new MemChunk out of this and link it into
; the free list.
@37dCompute base address of new MemChunk.
@37dStore pointer to next MemChunk.
@37dLink to previous MemChunk.
@37dStore number of bytes in MemChunk.
; At this point, whatever was left after the block was taken
; care of, and a pointer to the next MemChunk after the block is
; in A0.
@37dCompare start addresses.
@d
; The block started after the beginning of this MemChunk, so we
; just truncate the MemChunk to contain the leftover memory before
; the allocated block.
@d
@37dCompute remaining size.
@37dStore in the MemChunk.
@37dAll done.
; The block started exactly at the beginning of the MemChunk, so
; the MemChunk must be deleted.
@37dUnlink this MemChunk from the chain.
@37dReturn address of allocated block.
@d
@37dPermit()
@d
@37dReturn zero if allocation failed.
@d
---------------------------------------------------------------------------
size = AvailMem( requirements )
D0 D1
---------------------------------------------------------------------------
@d
@36dInitialize free memory counter.
@36dPoint at the ExecBase->MemList.
@36dForbid()
@d
@36dGet address of first MemHeader.
@36dExit if list is empty.
@36dGet this MemHeader's attributes.
@36dMask with required attributes.
@36dCheck if all attributes are satisfied.
@36dGo to next MemHeader if not.
@36dAre we looking for the largest
contiguous block of memory?
@d
@36dIf not, just add the size,
@36dand loop back.
; We are looking for the largest contiguous block of memory.
@36dGet address of first MemChunk.
@36dLoop back if there is no MemChunk.
@d
@36dSee if this MemChunk is the biggest yet.
@d
@36dIf it is, keep its base address (why?),
@36dand its size.
@d
@36dGo to next MemChunk.
@36dPermit()
@36dReturn the accumulated or largest size.
@d
@d
---------------------------------------------------------------------------
memList = AllocEntry( memList )
D0 A0
---------------------------------------------------------------------------
@d
@36dSave pointer to request MemList.
@36dClear loop counter.
@d
@36dGet the number of MemEntries.
@d
@36dCompute the size of the MemList needed
@36dto keep track of the MemEntries.
@36dSet MEMF_CLEAR.
@36dAllocate that amount of memory.
@36dIf we didn't get it, go exit.
@d
@36dPoint to base of new MemList structure.
@36dPut in the number of MemEntries.
@36dPoint to MemEntries in request MemList.
@36dPoint to MemEntries in result MemList.
For each MemEntry do
@36d Get its required attributes.
@36d Get its required size.
@36d Store the size in the result list.
@36d If size is not zero then
@36d Allocate the memory.
@36d Go exit if unsuccessful.
@36d Endif
@36d Store memory address in result list.
@36d Go to next request MemEntry.
@36d Go to next result MemEntry.
@36d Increment loop counter.
@36d Compare to number of MemEntries.
@36dEndfor.
@d
@36dCompute -(size of MemEntry list).
@36dPoint A2 back at the request MemList.
@36dPoint A3 back at the result MemList.
@36dReturn the address of the result MemList.
@d
@d
; Enter here if one of the MemEntries could not be allocated.
@36dSave size of the allocate which failed.
@36dTest number of allocated MemEntries.
@36dExit if none remaining.
@36dDecrement number of MemEntries.
@36dPoint to the allocated MemEntry.
@36dGet the address and size of the block
@36dcorresponding to it.
@36dDeallocate the block.
@36dGo back and check for more MemEntries.
; Enter here if the AllocEntry failed, after deallocating anything
; which was allocated before the failure.
@36dGet size of the allocate which failed.
@d
@36dSet the high bit.
@36dReturn it to the user.
---------------------------------------------------------------------------
FreeEntry( memList )
A0
---------------------------------------------------------------------------
@d
@36dPoint to the MemList.
@36dPoint to the first MemEntry.
@36dGet number of MemEntries.
@36dFor each MemEntry in the MemList do
@36d Get the address of the memory block.
@36d Get the sizde of the memory block.
@36d If size is not zero then
@36d Deallocate the block.
@36d Endif
@36dEndfor
@d
@36dGet the number of MemEntries again.
@d
@36dCompute the size of the MemList.
@36dPoint to the base of the MemList.
@36dDeallocate it.
@d
@d
---------------------------------------------------------------------------
error = AddMemList( size, attributes, pri, base, name )
D0 D0 D1 D2 A0 A1
---------------------------------------------------------------------------
; This routine takes a block of memory, builds a MemHeader at the
; start of it, and puts the rest of the block into the MemHeader's
; free list. Then it adds the MemHeader to the system free memory
; list.
@36dStore the name in the MemHeader.
@36dCompute first address after MemHeader.
@36dMemHeader's node type is NT_MEMORY.
@36dSet the MemHeader's priority.
@36dSet the memory attributes.
@d
@36dRound the base address of the block up
@36dto the nearest multiple of 8.
@d
@36dCompute by how much it changed,
@36dand add this amount to the length.
@36dTruncate the length to a multiple of 8.
@36dSubtract the size of the MemHeader.
@36dStore base address as lower bound of
@36dmemory and link to first MemChunk.
@d
@36dCompute upper bound address.
@36dStore it in the MemHeader.
@36dStore the amount of free memory.
@36dBuild a MemChunk at the start of free
@36dmemory (clear link, store size).
@36dGet base address of MemHeader
@36dPoint to the system free memory list.
@36dPut the MemHeader on the list.
@d
@8p@28wPadding.
; Data table used to build the exec jump vector table.
; There are 105 entries, corresponding to the 105 exec functions,
; followed by a -1 table end marker. Each entry is a 16-bit
; relative offset from the start of the table.
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@9l@9l@9l@l
@7p@w
@7p@29wEnd of table marker.
@7p@29wLooks like garbage.
@7p@w
---------------------------------------------------------------------------
AddPort( port )
A1
---------------------------------------------------------------------------
@36dAddress of port's message list.
@36dInitialize the list's head pointer.
@d
@36dInitialize the list's Tail field.
@36dInitialize the list's TailPred pointer.
@36dAddress of the public message port list.
@36dSafely add port to the list.
---------------------------------------------------------------------------
RemPort( port )
A1
---------------------------------------------------------------------------
@a?fc1b30
; Call a general purpose subroutine which unlinks something from
; its queue while disabling multitasking.
@d
---------------------------------------------------------------------------
PutMsg( port, message )
A0 A1
---------------------------------------------------------------------------
@36dMake sure message node type is NT_MESSAGE
@d
@36dCompute address of port's message list
@36dDisable()
@d
; Enqueue the message at the end of the port's message list
@36dPoint A0 at port's Tail field.
@36dSave pointer to last node in list
@36dMake new node the last node
@36dNew node has no next node
@36dLink new node back to old last node
@d
@36dLink old last node to new node
; Do whatever needs to be done on message arrival.
@d
@36dGet address of message port's task.
@36dExit if port has no task.
@36dGet port's flag bits.
@d
@36dDecode the flag bits.
@d
@d
; Processing for PA_SOFTINT
@d
@36dCause the software interrupt.
@d
; If PA_IGNORE, do nothing.
@d
@d
; (Undocumented?) processing for mp_Flags = 3. Apparently,
; calls a subroutine at mp_SigTask.
@d
@d
@d
; Processing for PA_SIGNAL
@36dGet SigBit.
@d
@36dCompute corresponding signal mask.
@d
@d
@36dSignal the task.
@36dEnable()
@d
@d
@d
---------------------------------------------------------------------------
message = GetMsg( port )
D0 A0
---------------------------------------------------------------------------
@36dPoint A0 at head of port's message list.
@36dDisable()
@d
; Try to dequeue a message from the message list.
@36dGet head pointer into A1.
@d
@36dIf list is empty, just return zero.
@d
@36dUnlink the message from the list.
@d
; Reenable interrupts if necessary and exit.
@36dEnable()
@d
@d
@d
---------------------------------------------------------------------------
ReplyMsg( message )
A1
---------------------------------------------------------------------------
@36dGet address of reply port.
@d
@36dNo reply port. Set node type to
@36dNT_FREEMSG and exit.
@36dSet node type to NT_REPLYMSG.
@d
@36dSend the message via the reply port.
---------------------------------------------------------------------------
message = WaitPort( port )
D0 A0
---------------------------------------------------------------------------
@36dGet head pointer of port's message list.
@36dCheck if list is empty.
@36dIf not empty, return right away.
@36dList was empty. Get signal bit.
@36dPoint A0 at message list.
@d
@36dCompute signal mask.
@d
@d
@36dWait()
@d
@36dCheck message list.
@36dIf still empty, go back and wait again.
@d
@36dReturn first message in the list.
@d
---------------------------------------------------------------------------
port = FindPort( name )
D0 A1
---------------------------------------------------------------------------
@36dPoint to the public port list.
@36dSearch it for a port of the given name.
@d
---------------------------------------------------------------------------
AddResource( resource )
A1
---------------------------------------------------------------------------
@36dPoint to the resource list.
@36dAdd the resource to the list.
---------------------------------------------------------------------------
RemResource( resource )
A1
---------------------------------------------------------------------------
@36dUnlink the resource from the list.
---------------------------------------------------------------------------
resource = OpenResource( resName )
D0 A1
---------------------------------------------------------------------------
@36dPoint to the resource list.
@36dForbid()
@36dFindName()
@36dPermit()
@d
@8p@28wPadding.
---------------------------------------------------------------------------
AddTask( task, initialPC, finalPC )
A1 A2 A3
---------------------------------------------------------------------------
@d
@36dMake the task state TS_ADDED.
@36dClear the task's flags.
@36dSet IDNestCnt and TDNestCnt to -1.
@d
@36dTask is not waiting for any signals.
@36dTask has not received any signals.
@36dTask does not have any exception signals.
@36dSet allocated traps.
@36dNo traps are enabled.
@36dInstall default system exception handler
@36dif the user has not provided one.
@d
@d
@d
@d
@36dGet the initial stack pointer.
@36dPut the exit code address on the stack.
@d
@36dUse system default if no exit code.
@36dClear 15 longwords below the process's
@36dinitial stack pointer, so all registers
@36dwill be zero when initially loaded.
@36dClear the initial status register.
@36dPut the initial PC on the stack.
@36dIf we have a 68881, then put another
@36dlongword on the stack.
@d
@d
@36dPut the initial stack pointer back.
@d
@36dDisable()
@d
@36dMake the task state TS_READY.
@36dPut the task on the TaskReady queue.
@d
@36dEnable()
@d
@d
@36dIf the task ended up at the front of the
@36dTaskReady queue, then
@36dReschedule()
@d
; System default task exit function
; ---------------------------------
@36dGet ExecBase.
@d
@36dAsk for current task to be removed.
; fall through into RemTask.
---------------------------------------------------------------------------
RemTask( task )
A1
---------------------------------------------------------------------------
@d
@36dCheck address of task to be removed.
@36dBranch if not zero.
@36dIf zero, use the current task.
@d
@36dIf not zero, check if it was the current
@36dtask anyway.
; The following is executed only if the task to be removed is not
; the current one. This is because the currently running task isn't
; on a queue anywhere.
@36dDisable()
@d
@36dUnlink the task from its queue.
@d
@36dEnable()
@d
@36dGet the address of the task back.
@36dMake the task TS_REMOVED.
@36dCheck if it is the current task.
@d
@36dIf so, disable task switching now.
@36dPoint to allocated memory list.
@d
@36dExit if the head pointer is zero,
@36dor if the tail pointer points back at
@36dthe list header.
@36dZero the list head pointer.
; Loop: Release all the memory blocks which the task had allocated.
@36dGet pointer to current list node.
@36dGet pointer to next list node.
@36dExit if the end of the list was reached.
@36dFreeEntry() this list node.
@36dBack to the head of the loop.
@36dCheck if the current task was removed.
@36dIf not, indicate success and exit.
@d
@36dGo to supervisor mode.
@36dPop exception data from the stack.
@36dDispatch() a new task.
@36dIf the current task was not removed,
@36dindicate success and return.
@d
---------------------------------------------------------------------------
task = FindTask( name )
D0 A1
---------------------------------------------------------------------------
@36dSee if a name was given.
@d
@36dIf not, use this task.
@d
@36dPoint at the TaskReady queue.
@36dDisable()
@d
@36dCall FindName() to look for the task.
@36dCheck if found, return address if so.
@d
@36dTask not found, try TaskWait queue.
@36dCall FindName() to look for the task.
@36dCheck if found, return address if so.
@d
@36dStill not found, compare name with that
@36dof the current task.
@d
@36dIf names differ, return zero (not found).
@d
@d
@36dTask was the current task.
@36dEnable()
@d
@d
@d
---------------------------------------------------------------------------
oldPriority = SetTaskPri( task, priority )
D0 A1 D0
---------------------------------------------------------------------------
@36dDisable()
@d
@36dSave the task's current priority.
@36dSet the task's new priority.
@36dCheck if it was the current task.
@36dIf so, go reschedule it.
@36dCheck if the task was TS_READY.
@36dIf not, we're done.
@d
@36dTake the task out of the TaskReady queue.
@d
@36dAnd put it back in the right place
@36dcorresponding to its new priority.
@36dSee if it ended up at the front.
@36dIf not, we're done.
; If we get here, either we've changed the priority of the current
; task, or we've moved another task to the front of the TaskReady
; queue. Either way, we have to check if the current task should
; be preempted.
@36dReschedule()
@d
@36dEnable()
@d
@d
@36dReturn the previous priority.
@d
---------------------------------------------------------------------------
oldSignals = SetExcept( newSignals, signalMask )
D0 D0 D1
---------------------------------------------------------------------------
@36dGet pointer to current task.
@36dPoint to its SigExcept longword.
@36dDrop into SetSignal.
---------------------------------------------------------------------------
oldSignals = SetSignal( newSignals, signalMask )
D0 D0 D1
---------------------------------------------------------------------------
@36dGet pointer to current task.
@36dPoint to its SigRecvd longword.
; This code is common to SetExcept and SetSignal.
@36dMask out signals to be left alone.
@36dDisable()
@d
@36dSave current value of signal word.
@36dMake mask of singnals to leave alone.
@36dGet those signals only.
@36dOr in the changed signals.
@36dPut the result back in the task.
; Now we drop into Signal(), which will process any new signals
; we may have set in the SigRecvd longword.
@36dGet the SigRecvd data.
@36dDrop into Signal.
---------------------------------------------------------------------------
Signal( task, signals )
A1 D0
---------------------------------------------------------------------------
@36dGet the set of received signals.
@36dDisable()
@d
@36dStore the signals.
@36dOr the new signals into the old ones.
; The code from here down is common to SetExcept, SetSignal, and
; Signal. It checks if the task whose signals are being modified
; should be awakened.
@36dCheck the exception signals,
@36dand see if any of them have become set.
@36dIf so, go process them.
@36dSee if the task is in the TS_WAIT state.
@36dIf not, we're done.
@36dSee if a signal being waited for has been
@36dset. If not, we are done.
; We have set a signal which the task was currently waiting for,
; so we must awaken it. First, we take it out of the TaskWait
; list then we make it TS_READY, then we put it on the TaskReady
; queue and check if it should preempt the current task.
@d
@36dSave pointer to the task.
@36dGet pointer to the next task in the list.
@36dGet pointer to the previous task.
@36dUnlink from the previous task.
@36dUnlink from the next task.
@36dRestore pointer to the task.
@36dMake the task TS_READY.
@36dPoint to the TaskReady queue.
@36dEnqueue the task in the right place.
@36dCheck if it was put at the head of the
@36dTaskReady queue, and exit if not.
@d
@36dEnable()
@d
@36dReturn old signals to caller eventually.
@36dReschedule() so the task can run.
; Enter here if we have set an exception signal, or if a previously
; set signal has become an exception signal.
@36dSet the task's TB_EXCEPT flag.
@36dSee if it is in the TS_WAIT state.
; If the task for which the exception occurred was in the TS_WAIT
; state, make it TS_READY and move it from the TaskWait list to
; the TaskReady queue. Otherwise, just dispatch it.
@d
@d
; Exit here if the current task was allowed to keep running (i.e.
; the other task did not preempt it).
@36dEnable()
@d
@d
@36dReturn old signals.
@d
---------------------------------------------------------------------------
signals = Wait( signalSet )
D0 D0
---------------------------------------------------------------------------
@36dGet the pointer to the current task.
@36dRecord which signals we are waiting for.
@36dDisable()
@d
@36dEnter the loop at the bottom.
; Main loop: We stay here until we have a signal we want.
@36dMake the task state TS_WAIT.
@36dPoint to the TaskWait list.
@d
@36dUnlink the task from the TaskReady
@36dqueue, and put it in the TaskWait
@36dlist.
@d
@d
@d
; Here we block. Calling Switch() gives control to any other
; process. Eventually, someone else doing a Signal() will put us
; back on the TaskReady list, and then we will return from the
; Switch() when our priority comes up again.
@36dSave A5.
@36dGet ready to call Switch(), but first...
@36dEnter Supervisor mode.
; We're back. See if we now have a signal we want.
@36dRestore A5.
@36dGet pointer to current process.
@36dGet the signals we are waiting for.
; We initially enter here to see if we have to block at all, and
; again each time we have been unblocked to see if we can now
; continue running.
@36dGet the signals which are currently set.
@36dCheck against signals to wait for.
@36dIf not, go back and block again.
; Eventually, we end up here. We now have one or more signals
; that were being waited for. We take these out of the set of
; received signals, since we will return them to the caller.
@36dUpdate the set of received signals.
@d
@36dEnable()
@d
@36dReturn the signals.
@d
---------------------------------------------------------------------------
Reschedule()
---------------------------------------------------------------------------
; This function is used when it is possible that a task should
; be preempted and another task run in its place. It sets the
; scheduling attention flag to force the scheduler to do its thing
; as soon as possible. If multitasking is disabled, that's all
; it does, since Permit() will do the rest when multitasking is
; turned on again. If interrupts are disabled, it sets the software
; generated interrupt. Since this is not accompanied by setting
; the software interrupt pending flag, it will do nothing but
; run ExitIntr() as soon as the interrupt level allows it.
; ExitIntr() will then cause the outstanding scheduling operation
; to be done.
; Note that part of Permit() is used to switch into supervisor
; mode and call the scheduler.
@36dSet the scheduling attention flag.
@36dSave its previous state.
@36dCheck if multitasking enabled.
@36dIf not, exit.
@36dCheck if interrupts enabled.
@36dIf so, go and do the scheduling.
@36dCheck the (old) scheduling attn flag.
@36dExit if it was already set.
@36dAssert the software generated interrupt.
@d
---------------------------------------------------------------------------
Forbid()
---------------------------------------------------------------------------
@36dIncrement the TDNestCnt.
@d
---------------------------------------------------------------------------
Permit()
---------------------------------------------------------------------------
@36dDecrement the TDNestCnt.
@36dExit if still positive.
@36dCheck if interrupts disabled.
@36dExit if yes.
; The current task has just reenabled multitasking. If the system
; is waiting to switch tasks, now is the time to do it.
@36dCheck the scheduling attention flag.
@36dExit if not pending.
@36dSave A5.
@36dSet address to go to in supervisor mode.
@36dEnter supervisor mode.
@36dRestore A5 and return.
@d
; This is executed in supervisor mode.
@36dCheck caller's supervisor mode flag.
@36dIf the caller was in supervisor mode,
@36dthen don't switch tasks.
; Eventually, this task will be dispatched again, using the program
; counter and status register left on the stack by the supervisor
; mode call. Then it continues running above, pops A5 from the
; stack, and returns to the caller.
@36dGo and do the scheduling.
---------------------------------------------------------------------------
trapNum = AllocTrap( trapNum )
D0 D0
---------------------------------------------------------------------------
@36dGet pointer to the current task.
@36dGet the task's TrapAlloc flags.
@36dSee if we want a particular trap number.
@d
@36dIf so, set this trap number's flag bit.
@36dIf it wasn't set, indicate success.
@36dIt was already set, indicate failure.
; Look for a free trap number.
@36d16 traps to check. Start at #15.
@36dTry to set this trap's flag bit.
@36dIf not set before, indicate success.
@36dLoop until all flags checked.
@36dIndicate failure.
@36dPut updated trap flags back.
@d
---------------------------------------------------------------------------
FreeTrap( trapNum )
D0
---------------------------------------------------------------------------
@36dGet pointer to the current task.
@36dGet the task's TrapAlloc flags.
@36dClear the specified flag.
@36dPut the flags back.
@d
---------------------------------------------------------------------------
signalNum = AllocSignal( signalNum )
D0 D0
---------------------------------------------------------------------------
@36dGet pointer to the current task.
@36dGet the task's SigAlloc flags.
@36dSee if we want a particular signal.
@d
@36dIf so, try to set its flag.
@36dIndicate success if it was clear.
@36dIndicate failure if already set.
; Look for a free signal.
@36d32 Signals to check. Start at 31.
@36dTry to set the signal's flag.
@36dIf it wasn't already set, success.
@36dOtherwise, keep looking.
@36dIndicate failure.
@d
@36dSuccess. Update allocated signal flags.
@d
@36dSet all but this signal bit in D1.
@36dMake sure this signal isn't set.
@36dMake sure it's not an exception signal.
@36dIndicate that we are not waiting for it.
@d
---------------------------------------------------------------------------
FreeSignal( signalNum )
D0
---------------------------------------------------------------------------
@36dGet pointer to the current task.
@36dGet the SigAlloc flags.
@36dClear the indicated signal bit.
@36dPut the flags back.
@d
; Subroutines for RawDoFmt()
; --------------------------
; Find the length of a null-terminated string pointed to by A0.
; For efficiency, the length is counted up as a negative number.
@a?fc200c
@40dStart the length at -1.
@40dGet string character, test for end.
@40dCount, and loop until end of string.
@d
@40dConvert length to correct +ve value.
@d
; Evaluate a decimal numeric constant pointed to by A4.
@40dStart the result at 0.
@d
@40dGet a character from the input.
@a?fc2020
FC2020 cmp.b #'0',D2
@a=fc2024
@d
FC2026 cmp.b #'9',D2 If not a numeric digit, exit.
@a=fc202a
@d
@40dMultiply previous result by 10.
@d
@d
@d
@d
@a?fc2036
FC2036 sub.b #'0',D2 Convert digit to number 0 - 9.
@a=fc203a
@40dAdd to result.
@40dGo evaluate next character.
; Finish up by pointing back at the non-digit character.
@40dBackspace the input pointer.
@d
; Convert the number in D4 to its decimal ASCII representation.
@40dIs the number zero?
@40dIf so, just output "0" and return.
@40dIs it negative?
@40dIf not, make it negative.
@d
@a?fc204c
FC204C move.b #'-',(A5)+ If so, output a "-".
@a=fc2050
; The minus sign is taken care of, and negative the number to
; be output is in D4.
@40dPoint at table of divisors.
@40dClear the "non-zero digit" flag.
@40dGet a divisor from the table.
@40dIf zero, output last digit and exit.
; Subtract the current divisor from the number as many times as
; possible (actually, add to its negative).
@40dStart the counter at -1.
@40dTry to add the divisor to D4.
@40dLoop until D4 is greater than zero.
@40dMake the number less than zero again.
@40dIncrement the counter by 1.
; D0 now contains negative the current digit to output. Discard
; it if it is a leading zero.
@40dIf the current digit is zero then
@40dif no non zero digits have been
@40doutput, then discard it.
; Output the digit.
@40dSet "non-zero digit" flag.
@40dMake D0 positive.
@a?fc2072
FC2076 add.b #'0',D0 Convert to an ASCII digit.
@a=fc2076
@40dPut it into the buffer.
@40dGo do the next digit.
; Enter here to output the last digit, or the single "0" if the
; number is zero.
@40dMake D0 positive.
@a?fc207c
FC207C add.b #'0',D4 Convert to an ASCII digit.
@a=fc2080
@40dPut it into the buffer.
@d
; Table of divisors.
@8p@32l1000000000 decimal.
@8p@33l100000000 decimal.
@8p@34l10000000 decimal.
@8p@35l1000000 decimal.
@8p@36l100000 decimal.
@8p@37l10000 decimal.
@8p@38l1000 decimal.
@8p@39l100 decimal.
@8p@40l10 decimal.
@8p@41l0 decimal.
; Convert the number in D4 to its hexadecimal ASCII representation.
@a?fc20ac
@40dIs the number zero?
@40dIf so, just output "0" and exit.
@40dClear "non-zero digit" flag.
@40dIs the data 32 bits long?
@d
@40dIf not, output 4 digits,
@40dfrom the high word of D4.
@d
@40dIf so, output 8 digits.
; Digit output loop.
@40dRotate leftmost digit into bits 0-3.
@d
@40dMask out all but bits 0-3.
; Skip leading zeros.
@40dIf no non-zero digit has been
@40dencountered yet, and this is a zero,
@40dskip it.
@40dSet "non-zero digit" flag.
@40dIs the digit greater than 9?
@d
@a?fc20d6
FC20D6 add.b #'0',D0 If not, convert to ASCII numeral.
@a=fc20DA
@d
FC20DC add.b #$37,D0 If so, convert to upper case letter.
@a=fc20e0
@40dPut digit in buffer.
@40dLoop until all digits done.
@d
---------------------------------------------------------------------------
RawDoFmt( FormatString, DataStream, PutChProc, PutChData )
A0 A1 A2 A3
---------------------------------------------------------------------------
@d
@40dReserve stack space for buffer.
@40dStore data stream pointer.
@40dPoint to the format string.
@40dGet a byte from the format string.
@40dCheck for end and exit if found.
@a?fc20f8
FC20F8 cmp.b #'%',D0 Check for format specifier.
@a=fc20fc
@40dProcess it if found.
@40dOtherwise, output the character,
@40dand go on to the next one.
@40dOutput the terminating zero.
@40dDeallocate the output buffer.
@d
@d
; Enter here if the "%" format specifier was found. Next we check
; for the characters "-", signaling that the printed item should
; be left-aligned in its field, and "0", signaling that leading
; zeros should be attached.
@40dPoint to the output buffer.
@40dClear the option flags.
@a?fc2112
FC2112 cmp.b #'-',(A4) Left alignment desired?
@a=fc2116
@d
@40dIf so, set the corresponding flag,
@40dand go on to the next character.
FC211E cmp.b #'0',(A4) Zero fill desired?
@a=fc2122
@d
@40dIf so, set the corresponding flag.
@40dGet the field width.
@40dStore it in D6.
@40dAssume no maximum length.
@a?fc2130
FC2130 cmp.b #'.',(A4) Maximum length specifier?
@a=fc2134
@d
@40dIf so, go on to the next character,
@40dand get the maximum length.
@40dStore maximum length.
FC213E cmp.b #'l',(A4) 32 bit data?
@a=fc2142
@d
@40dIf so, set the corresponding flag,
@40dand go on to the next character.
; At this point, we have interpreted a format string of the format
; "%-0xxx.yyyl". In D3, bit 0 is set if the "-" was present, bit 1
; is set if the "0" was present, and bit 2 is set if the "l"
; was present. D6 contains the value of "xxx" (numeric constant),
; and D5 contains the value of "yyy" if present, or 0 if not.
; Now we process the actual format characters: "s" for string, "c"
; for character, "d" for decimal, "x" for hex. In all cases, the
; goal is to build a null-terminated string, pointed to by A0,
; containing the formatted output.
@40dGet the next character.
@a?fc214c
FC214C cmp.b #'d',D0 Decimal output?
@a=fc2150
@40dSkip to next option if not.
@40dGet data item.
@40dFormat as decimal number.
@40dOutput the buffer.
FC215A cmp.b #'x',D0 Hexadecimal output?
@a=fc215e
@40dSkip to next option if not.
@40dGet data item.
@40dFormat as hexadecimal number.
@40dOutput the buffer.
; Subroutine to get a number from the data stream. We get either
; a word or a longword, depending on whether an "l" was in the
; format specification.
@40dIf data size is 16 bits then
@d
@40d Get data stream pointer.
@40d Get a word from the data stream.
@40d Put data stream pointer back.
@40d Extend to longword.
@40dElse
@40d Get data stream pointer.
@40d Get a longword from data stream.
@40d Put data stream pointer back.
@40dEndif
; Continue here if not formatting a number.
@a?fc2188
FC2188 cmp.b #'s',D0 String output?
@a=fc218c
@40dIf not, skip to next.
@40dGet data stream pointer.
@40dPoint directly at the string.
@40dPut data stream pointer back.
@40dOutput the string.
FC2196 cmp.b #'c',D0 Single character output?
@a=fc219a
@40dIf not, discard format specifier.
@40dGet character from input stream.
@40dPut it into the buffer.
; Numeric and character output, having put their formatted argument
; into the buffer, meet here.
@40dZero-terminate the buffer.
@40dPoint back to its start.
; All output options continue here. A5 now points to a
; null-terminated string to output.
@d
@40dFind the length of the string.
@40dWas a maximum length specified?
@d
@40dIf so, and if the output string is
@40dlonger, set the length to the
@40dmaximum length.
@40dCompute amount of padding needed.
@d
@40dSet to zero if negative.
@40dWas left alignment desired?
@d
@40dIf not, output padding first.
@d
@40dLoop to copy the string to the output
@40duntil its end or the given maximum
@40dnumber of characters.
@40dWas left alignment desired?
@d
@40dIf so, output padding now.
@40dContinue with format string.
; Subroutine to output the right amount of padding.
@a?fc21de
FC21DE move.b #' ',D2 Assume padding with spaces.
@a=fc21e2
@40dWas zero-fill desired?
@d
FC21E8 move.b #'0',D2 If so, pad with zeros.
@a=fc21ec
@d
@40dLoop to output the required number
@40dof padding characters.
@d
@d
---------------------------------------------------------------------------
RawIOInit()
---------------------------------------------------------------------------
; Set the serial port for receiving 8 bit data at 9600 bps.
@40dSet up the SERPER register.
@d
---------------------------------------------------------------------------
RawMayGetChar()
---------------------------------------------------------------------------
@d
@40dRead SERDATR.
@40dIs a byte in the receive buffer?
@40dIf not, return -1 and exit.
@40dClear the serial receive interrupt.
@40dGet the received character.
@40dReturn it to the caller.
@d
; Subroutine to wait for a character on the serial port.
@40dRawMayGetChar()
@40dDid we get a character?
@40dContinue waiting if not.
@40dReturn the character.
; This looks like a C entry point for RawPutChar.
@40dGet the first C parameter.
---------------------------------------------------------------------------
RawPutChar()
---------------------------------------------------------------------------
; Don't output code 0, and expand newlines to CRLF.
@40dIs the character to send zero?
@40dIf so, don't send it.
@40dSave the character.
@40dIs it a newline?
@d
@40dIf so, send a carriage return first.
@d
@40dGet the character back.
; Enter here to send the character in D0 on the serial port.
@40dRead SERDATR.
@40dTransmitter ready?
@40dWait until true.
@40dMask out all but bits 0-7.
@40dSet the stop bit.
@40dWrite to SERDAT.
; Handle XON/XOFF and/or escape into the debugger if DEL pressed.
@40dRawMayGetChar()
@40dDid we get an XOFF?
@d
@40dIf yes, wait for any other character.
@d
@40dDid we get a DEL?
@40dReturn if not.
@40dIf so, Debug()
@40dOn return, check for XOFF again.
@d
; C compatible routine to print a string.
@40dGet first C parameter (string addr.)
; Assembly language entry point (address in A0).
@40dGet a string character.
@40dExit if is the terminating zero.
@40dIs it a newline?
@d
@40dIf so, output a CR first.
@d
@d
@40dOutput the character.
@40dGo on to next character.
@d
; C compatible function to output a hex number. First argument
; is the number, second one is the number of digits. The number
; will be output with one space following.
@40dGet C parameters.
; Assembly language entry point.
@d
@d
@d
@40dCompute 8 - number of digits.
@40dLoop that many times, rotating the
@40dnumber left by a digit each time,
@40dto left-align the number in D2.
@40dGet number of digits.
@d
@40dShift current digit into bits 0-3.
@d
@40dExtract bits 0-3 from the number.
@40dNumeric or alphabetic digit?
@d
@40dIf alphabetic, add ('A'-'9').
@a?fc22ba
FC22BA add.b #'0',D0 Convert to ASCII digit.
@a=fc22be
@40dOutput the digit.
@40dLoop until all digits done.
@d
@40dOutput a space.
@d
@d
; Entry point to do a RawDoFmt() to the serial port.
@40dSave A2.
@40dPoint A2 to RawPutChar() function.
@40dRawDoFmt()
@40dRestore A2.
@d
---------------------------------------------------------------------------
Open()
---------------------------------------------------------------------------
; This gets executed if someone actually opens "exec.library".
@40dReturn ExecBase.
@40dIncrement exec.library open count.
@d
---------------------------------------------------------------------------
Close()
---------------------------------------------------------------------------
; This gets executed if someone tries to close "exec.library".
@40dDecrement exec.library open count.
---------------------------------------------------------------------------
Expunge()
---------------------------------------------------------------------------
; This gets executed if someone tries to Expunge() the exec.
; Needless to say, we won't do anything of the sort, but we'll make
; the caller happy by returning zero.
; The reserved jump vector (at ExecBase - 24) also points here.
@d
@d
@a?fc22f0
; ROM-Wack
; --------
; This is the Amiga's ROM resident mini-debugger. The following
; string is sent out the serial port when it starts up.
@8p@,10s
; ROM-Wack has a private, 236 byte data area at $000200. The
; following is a memory map of this area. Addresses are given as
; hex offsets from $000200.
;-------------------------------------------------------------------
; 00 (32 bit) Pointer to current key bindings.
; 04 (32 bit) Saved key binding pointer if not using main ones.
; 08 (32 bit) Value of last number entered by the user.
; 0C (32 bit) The "current address" for all operations.
; 10 - 13 (not used).
; 14 (32 bit) The current "frame size".
; 18 (32 bit) The upper limit address for searches and fills.
; 1C (16 bit) Number of characters in the input buffer.
; 1E (8 bit) Flag indicating whether the "frame" should be
; redisplayed after a command has executed.
; 1F (8 bit) Flag indicating whether we are in "alter" mode.
; 20 (16 bit) Flag indicating whether there is unprocessed data
; in the buffer (0 if yes, 1 if no).
; 22 (16 bit) Number of digits in the number most recently entered.
; 24 (16 bit) Flag indicating whether a number is being gathered as
; a parameter to a command, or being entered unprompted
; (in which case it becomes the current address).
; 26 (32 bit) Indirection stack pointer (for following pointers).
; 2A - 4F (not used).
; 50 - 81 Input buffer for user commands.
; 82 (16 bit) Last character typed by the user.
; 84 (32 bit) Pointer to data area on the stack, holding CPU and
; process related information (map farther down)
; 88 (16 bit) Instruction to use for breakpoints (TRAP #15).
; 8A - E9 Breakpoint table. Each entry consists of an address
; where an instruction was replaced with TRAP #15, and
; the word which had been there before.
; EA (16 bit) Value to be written to INTENA to restore serial
; port interrupts to their original state.
;-------------------------------------------------------------------
; This gets called from the exec initialization code.
@40dSave ExecBase.
@40dPoint to ROM-Wack's data area.
@40dInitialize it.
@40dRestore ExecBase.
@40dInstall default ExecBase->DebugEntry.
@40dInstall default Debug() vector.
@40dRawIOInit()
@d
; Special exception handlers for ROM-Wack
; ---------------------------------------
; ROM-Wack trace exception handler.
@40dPush "Trace" exception number.
@40dEnter the debugger.
; ROM-Wack breakpoint exception handler. A "TRAP #15" instruction
; is used for the breakpoint.
@40dPush "TRAP #15" exception number.
@40dEnter the debugger.
---------------------------------------------------------------------------
Debug()
---------------------------------------------------------------------------
; This entry point is used when an actual function call to Debug()
; is made. We set up the supervisor stack to look like it would
; after an exception handled by the exec exception entry points.
@40dSave A5.
@40dWhere to go in supervisor mode.
@40dSupervisor()
; We now have an exception stack frame from the Supervisor() call.
@40dPop the return address from the
@40dDebug() call from the user stack.
@d
@40dGet the return address back.
@40dFake exception number zero.
; Now the supervisor and user stacks look as if an exception had
; occurred and was handled by the exec, with the difference that
; the original value of A5 is on the supervisor stack, and A5 now
; contains the return address from the Debug() call.
; Exception entry point.
; ----------------------
; The debugger is entered here with the stacks already set up as
; above (by the system exception handler). First, we verify if
; the stack is even working (pointing at RAM).
@40dPut a signature on the stack.
@40dCheck if it can be read back.
@d
; A signature pushed on the supervisor stack could not be read
; back, so we are in serious trouble. Initialize the stack at
; the top of the first 256K of chip memory. Put a fake exception
; stack frame on the new stack.
@40dStart stack at 256K.
@40dFake program counter.
@40dFake status register.
@40dPush exception number -1.
@d
; We now have a working supervisor stack with the exception
; number and stack frame on it.
@40dSave most of the CPU registers.
@40dPoint A5 at the exception number.
@40dReserve 22 bytes of stack space,
@40dand point A4 to the bottom of this.
@d
@40dGet the exception number.
@40dStore it.
@40dStore supervisor stack pointer.
@d
@40dStore user stack pointer.
; When a 680x0 hits an exception, it pushes the program counter, then
; the status register, onto the supervisor stack. For bus and
; address errors, however, more information is saved. On 68010 and
; 68020 processors, this comes before the program counter and status
; register, i.e. the stack pointer after an exception always points
; at these. On the 68000, however, 8 bytes of other information
; are pushed on the stack AFTER the PC and SP. The following code
; compensates for this.
@40dCheck CPU/FPP configuration.
@40dIs it a plain vanilla 68000?
@d
@40dWas it an address error?
@d
@40dWas it a bus error?
@d
@40dIf either, skip past bus error info.
; For all CPUs, A5 now points to where the status register and
; program counter are stored on the supervisor stack.
@40dCheck the supervisor mode bit.
@40dIf it was set,
@40dget ExecBase,
@40dand get the current task pointer.
@40dGet and store the status register.
@40dGet and store the program counter.
; Time for a summary (so I don't get confused). A4 currently
; points at a data structure, on the supervisor stack, containing
; the following:
; (60 bytes) Register dump (D0-D7, A0-A6).
; (32 bit) Current task pointer or zero.
; (32 bit) Number of the exception that got us here (see below).
; (32 bit) Saved supervisor stack pointer.
; (32 bit) Saved user stack pointer.
; (16 bit) Saved status register (from exception stack frame).
; (32 bit) Saved program counter (from exception stack frame).
; If the exception number is zero, Debug() was used to get here.
; If the exception number is -1, we somehow got here, but the
; supervisor stack pointer was clobbered and not pointing to RAM.
; In this case, we have just set it to 256K, and the saved
; program counter and status register are invalid.
@40dPoint to ROM-Wack's data area.
; Disable serial port interrupts, and make a control word which,
; when written to INTENA, will return them to their original status.
@40dStore interrupt enable status.
@40dDisable serial port interrupts.
@d
@40dMake the control word to be used
@40dto restore those two interrupts.
@40dRawIOInit()
@d
@40dPrint a newline and "rom-wack".
@40dSave pointer to stack-resident data.
@40dMake the saved exception program
@40dcounter even (if not already so),
@40dand put it back.
@40dSet the "current address" there.
@40dDid a TRAP #15 get us here?
@40dSkip the following if not.
; Special handling for breakpoints. Back the PC up by 2 (over
; the TRAP #15 instruction used for the breakpoint), and clear
; the breakpoint (restore the original instruction).
@40dBack up to breakpoint address.
@40d"clear" the breakpoint.
@40dUpdate the saved program counter.
@40dPoint to the stack-resident data.
; Install the exception vectors needed for breakpoints and
; single-stepping.
@40dInstall TRAP #15 exception vector.
@40dInstall "Trace" exception vector.
@40dSet the search limit to 16 megabytes.
@40dDisplay the ROM-Wack register frame.
@40dEnter the ROM-Wack main loop.
; Entry point for <Tab> command.
; Entry point to run a single instruction in trace mode. This does
; everything required to resume running as below, but with the trace
; mode bit set in the saved status register, so only a single
; instruction will be executed after the RTE (and then we come back
; in at the top).
@40dPoint to data on system stack.
@40dGet the saved status register, set
@40dthe trace mode bit, and store it.
@d
; Set up to continue running after exit from ROM-Wack. Note that
; to compensate for the different exception stack frame formats of
; bus error and regular exceptions, we don't even try to compensate
; for the format of the exception stack frame, just build one
; containing a simple status register and program counter right
; above the register dump. This allows the RTE instruction to
; conveniently resume running whatever called ROM-Wack, but
; some garbage may be left on the stack.
; Entry point for "go" command.
; This starts running wherever the current address is pointing.
@40dPoint to data on system stack.
@40dMake PC the current address.
; Entry point for "^D" and "resume" commands.
; This continues running where it left off.
@d
@40dGet the saved status register.
@40dClear the trace mode flag and put
it on the stack.
@40dRestore serial port interrupt status.
@40dPut the program counter on the stack.
@40dGet the saved user stack pointer,
@40dand restore it.
@40dPoint to the register dump.
@40dRestore all the other registers.
@40dPop the exception number.
@40dReturn from the exception.
; Subroutine to initialize ROM-Wack's data area.
@40dPoint to data area.
@d
@40dClear 236 bytes.
@d
@40dUse primary key bindings.
@40dFrame size = 16 bytes.
@40dUse "TRAP #15" for breakpoints.
@d
@8p@32wPadding.
; A lot of the functions in ROM-Wack have C compatible entry points
; (which get the parameters from the stack), and most of them have
; C compatible return values (in D0). I guess this is to interface
; them to other functions, written in C, in the bigger versions of
; Wack. Likewise, the following are probably C interface functions,
; but they (and most of the C entry points) aren't used anywhere.
; "Peek" function for C. Takes address, returns 16-bit contents.
@a?fc2498
@d
@d
@d
; I have no idea why this is here twice.
@d
@d
@d
; "Poke" function for C. Takes address and a word, and stores the
; word at the address given.
@d
@d
@d
; Entry point for "user" command.
; Takes all the data currently resident on the supervisor
; stack (exception stack frame, exception number, register dump,
; ROM-Wack data area, and any other stuff), and moves it onto the
; user stack. Then puts the CPU into user mode.
; In effect, this switches ROM-Wack from running as part of the
; exec kernel (in supervisor mode) to running as a plain, ordinary
; task, along with other tasks. The good side is that the system
; can now go and clean up (flush disk buffers, etc), while the
; user can go on playing with ROM-Wack. The bad side is that the
; task which did Debug() or trapped into ROM-Wack is stuck there
; forever. "go", and "resume" commands will no longer work.
@40dPoint to data area on stack.
@40dWas CPU in supervisor mode?
@40dIf not, exit.
@40dGet the user stack pointer.
@40dReserve 92 bytes on user stack.
@40dMake this the new data area.
@40dPoint to top of new data area.
@40dPoint to top of old data area.
@40dSave the supervisor stack pointer.
@d
@d
@40dCopy exception stack frame, register
@40ddump, data area, and any other stuff
@40dto the user stack.
@40dBump supervisor stack pointer up.
@40dBump user stack pointer down.
@d
@40dGet the saved status register.
@40dPrint a newline.
@d
; String compare function. This checks if a command entered by
; the user, pointed to by A1, matches a command, pointed to by A0,
; from the command table. It returns zero if so, else it returns
; the character number at which the mismatch occurred.
; C style entry point.
@d
; Assembler entry point.
@d
@40dGet a byte from reference string.
@40dEnd of string?
@40dCompare to byte from second string.
@40dLoop while strings are equal.
@40dCompute number of equal characters.
@40dExit.
@40dCheck if other string ends also.
@d
@40dReturn zero (strings match).
@d
; Entry point for <Return> command (Redisplay frame).
@40dRequest redisplay of current frame.
@d
; I don't know what this is for. It's not referenced anywhere.
@d
@d
@d
@d
; Entry point for ">", <Space> commands (Move forward a word).
@40dIncrement current address by 1 word.
@40dRequest frame display.
@d
@d
@40dPrint a newline.
@d
@d
@d
; Entry point for "<", <Backspace> commands (Move back a word).
@40dDecrement current address by 1 word.
@40dRequest frame display.
@d
@d
@40dPrint a newline.
@d
@d
@d
; Entry point for "." command (move forward a frame).
@40dGet frame size.
@40dAdd to current address.
@40dRequest frame display.
@d
; Entry point for "," command (move back a frame).
@40dGet frame size.
@40dSubtract from current address.
@40dRequest frame display.
@d
; Entry point for "[" command.
; This follows a pointer at the current address.
@40dGet indirection stack pointer.
@40dGet current location.
@40dStore in indirection stack.
@40dPut indirection stack pointer back.
@40dMake the current location even.
@40dGet the pointer.
@40dPut it in the current location.
@40dRequest display of frame.
@d
; Entry point for "]" command.
; This walks undoes a "[" command, good for walking back along
; singly linked lists or backing out of nested structure pointers.
@40dGet indirection stack pointer.
@40dGet address from indirection stack.
@40dRequest frame display.
@40dUpdate the indirection stack pointer.
@d
; Entry point for "+" command.
@40dEcho "+" on the user's terminal.
@d
@40dRead a number from the keyboard.
@d
@40dIf none, print newline and exit.
@40dGet the number which was entered.
@40dAdd to current address.
@40dRequest frame redisplay.
@d
; Entry point for "-" command.
@40dEcho "-" on the user's terminal.
@d
@40dRead a number from the keyboard.
@d
@40dIf none, print newline and exit.
@40dGet the number which was entered.
@40dSubtract from the current address.
@40dRequest frame redisplay.
@d
; The following isn't referenced anywhere. Perhaps an outdated
; bit of code to set the current address to the last number entered.
; This is now unnecessary.
@d
@d
@d
; Entry point for ":" command (set frame size).
@40dEcho ":" on the terminal.
@d
@40dGet a number from the terminal.
@40dSet the frame size.
@40dRequest frame redisplay.
@d
; Routine to print a prompt for memory-modify commands.
@40dGet the current address.
@40dPrint it (6 digits).
@40dIs the frame size zero?
@40dIf frame size is not zero, then
@d
@40d Get word at current address.
@40d Print it (4 digits).
@40d Print "=".
@d
@40dElse
@40d Print "xxxx ="
@40dEndif
@d
; Entry point for "=" command (modify one word of memory).
; This is also used by the "alter" routine below. It returns 1
; if a value was entered, and 0 if not.
@40dPrint a prompt.
@40dGet a number from the terminal.
@40dDid we get one?
@d
@40dIf so, update the word pointed
@40dto by the current address with the
@40ddata which was entered, and return
@40da non-zero value.
@40dAre we in "alter" mode?
@d
@40dIf not, request frame redisplay.
@d
@8p@,8s
; Entry point for "alter" command.
@40dSet "alter mode" flag.
@40dPrint a newline.
@40dDo an "=" command.
@d
@40dIf a value was entered, increase the
@40dcurrent location by 2, and loop back.
@d
@40dClear "alter mode" flag.
@40dPrint a newline.
@d
; I don't know what this is for. Perhaps a remnant from an old
; version of ROM-Wack, or something from a bigger version of Wack.
; It's not called from anywhere.
@a?fc2692
@40dPoint to the string below.
@40dPrint it.
@d
@8p@,22s
; Entry point for "list" command.
; This will traverse a standard exec list, displaying information
; about each node. It should be called when the current address is
; either that of the list header, or of any node.
@d
@40dGet the current address.
@40dList header ("Tail" field zero)?
@d
@40dIf yes, go to first node.
@40dSee if now at end of list (or empty).
@40dIf no more nodes, exit.
@40dGet the node's name.
@40dIf name pointer is null, replace
@40dit with a pointer to a zero.
@40dSave name pointer on stack.
@d
@40dGet priority field.
@40dPut it on the stack.
@40dGet type field.
@40dPut it on the stack.
@40dPut node address to stack.
@40dPoint to data on stack.
@40dPoint to format string below.
@40dRawDoFmt() to serial port.
@40dPop data back off the stack.
@40dMove to next node.
@40dLoop until no more nodes.
@40dPrint a newline.
@d
@d
@a?fc26fc
@8p@,36s
; Subroutine to display a "frame" of data from memory. On entry,
; the address of the frame is given in D0, and its size in D1.
; Data is displayed in lines of up to 8 words, and as an address,
; followed by hex words, followed by their ASCII character
; equivalents.
@d
@40dReserve 40 bytes of stack space.
@40dPrint a newline.
@d
@40dSet number of bytes remaining.
@40dJust exit if frame size is zero.
; Outer loop: Display one line of the frame.
@a?fc2732
@d
@40dPrint the address.
@40dMaximum 8 words per line.
@40dPoint to reserved stack space.
; Inner loop: Display one word of data.
@40dGet a word from memory.
@40dPrint it.
@d
@40dGet high 8 bits of current word.
@40dConvert to printable data.
@40dAdd to character string.
@d
@40dDo likewise for low 8 bits.
@d
@40dDecrement number of bytes remaining.
@40dExit if zero,
@40dotherwise continue until line full.
; End of line reached.
@40dMark end of character string.
@40dPoint to beginning.
@40dPrint the string and a newline.
@40dGo and do next line.
; End of frame reached.
@40dMark end of character string.
@40dPoint to beginning.
@40dPrint the string and a newline.
@d
@d
@d
; Subroutine to display an 8-digit hex number.
@d
@40d8 digits.
@d
; Subroutine to display a 6-digit hex number (address).
@d
@40d6 digits.
@d
; Subroutine to display a 4-digit hex number.
@d
@40d4 digits.
@40dGo print the number.
@d
@d
; Subroutine to print the "DR:", "AR:", and "SF:" portions of
; the ROM-Wack register frame.
@d
@d
@40d7 Data registers.
@40dPoint to "\nDR: " string.
@40dDisplay data registers.
@40d6 Address registers.
@40dPoint to "\nAR: " string.
@40dDisplay address registers.
@40dSkip exception number on stack.
@40dSupervisor mode?
@d
@40dIf not, get saved USP.
@d
@40dPoint to "\nSF: " string.
@40dPrint it.
@d
@40dDisplay 14 words of stack data.
@d
@d
@40dPrint a newline.
@d
@d
; Subroutine to print a string pointed to by A0, followed by (D2)
; longwords from memory pointed to by A2.
@40dPrint the string.
@d
@40dLoop and print all the data.
@d
@d
; Subroutine to print the first ("PC: ...") line of the register
; frame, followed by the rest of the register frame via the routine
; above. Note, we just give RawDoFmt() a format string describing
; the data structure on the supervisor stack
; Also entry point for "regs" command.
@40dGet pointer to stack-resident data.
@d
@40dPoint to format string.
@40dFormat the data.
@d
@40dPoint to the register dump area.
@40dPrint it.
; Text strings used to display register frames. The second one
; isn't used anywhere.
@a?fc27fc
@8p@,52s
@8p@,19s
@8p@,33s
@8p@,6s
@8p@,6s
@8p@,6s
; Memory fill routine. Fills (D1) words, starting at (D0), with
; the value in D2. Not used anywhere.
@d
@d
@d
@d
@d
@d
@d
; Subroutine to find a breakpoint in the breakpoint table, given
; its address. If successful, returns address of breakpoint table
; entry, if not, returns zero.
@a?fc2886
@40dPrint a newline.
@40dPoint to breakpoint table.
@d
@40dScan breakpoint table for a
@40dbreakpoint address matching A1.
@d
@d
@40dNot found, so return zero.
@d
@40dReturn address of table entry.
@d
; Entry point for "clear" command.
; Deactivate the breakpoint at the current program address,
; if there is one.
@40dGet current address.
@40dSee if it is a breakpoint address.
@d
@40dIf so, deactivate it and write the
@40doriginal instruction back.
@d
; Entry point for "reset" command.
; Deactivates all breakpoints.
@40dPoint to ROM-Wack's breakpoint table.
@40dSize of breakpoint table - 1.
@40dGet breakpoint address.
@40dIf breakpoint in use then
@d
@40d Clear breakpoint address.
@40d Fix instruction at breakpoint.
Endif
@40dGo to next table entry.
@40dLoop until whole table done.
@40dPrint a newline.
@d
; Entry point for "set" command.
; Activates a breakpoint at the current address.
@40dGet current address.
@40dSee if it is a breakpoint.
@40dIf so, do nothing.
@d
@40dScan the breakpoint table for an
@40dunused entry.
@d
@d
@d
; No unused breakpoint table entry found.
@40dPoint at "too many" string.
@40dPrint it.
@40dReturn.
; Unused entry in the breakpoint table found. Activate a breakpoint.
@40dSave instruction at breakpoint.
@40dReplace with TRAP #15.
@40dSave the breakpoint address.
@d
@8p@,12s
; Entry point for "show" command.
; Lists all active breakpoints.
@40dPoint to breakpoint table.
@40dLoop for 16 entries.
@40dGet current breakpoint address.
@40dSkip if zero.
@40dPrint a newline.
@40dPrint the breakpoint address.
@40dGo to next table entry.
@40dLoop until done.
@40dPrint a newline.
@d
@40dGarbage.
; Subroutine to get a character and echo it to the user.
@40dGet a character.
@d
@40dEcho it back.
@d
@d
; Entry point for "!" command (Modify a register).
@d
@40dPrint "!".
@d
@40dGet a character.
@40dConvert to upper case.
@40dGet pointer to stack-resident data.
@40dPoint to data register dump area.
@40dMaximum data register # = 7.
@a?fc2952
FC2952 cmp.b #'D',D0 [D]ata register?
@a=fc2956
@d
@40dPoint to address register dump.
@40dMaximum address register # = 6.
FC295E cmp.b #'A',D0 [A]ddress register?
@a=fc2962
@d
@40dPoint to user stack pointer.
FC2968 cmp.b #'U',D0 [U]SP?
@a=fc296c
@40dIf so, go change it.
@40dOtherwise, exit.
; Enter here with A0 pointing to a dump area containing sequential
; images of the address or data registers, and D2 containing the
; maximum register number.
@40dGet a character.
@40dBackspace?
@40dIf so, exit.
@40dIs it a digit?
@40dIf not, exit.
@40dConvert to number.
@40dGreater than maximum?
@40dIf so, exit.
@40dCompute offset to given register.
@40dCompute address of register.
; Enter here with A2 pointing to a longword containing the image
; of the register to modify.
@40dGet the current value.
@40dPrint a space.
@40dDisplay value as 8 hex digits.
@40dPrint "="
@d
@40dGet the new value from the user.
@40dWas a value entered?
@40dIf not, exit.
@40dUpdate the register.
@40dPrint a newline.
@d
@d
; Entry point for "^" and "limit" commands.
; This copies the current address to the "limit" address, i.e. the
; address to stop searching or filling memory at.
@40dCopy current address to limit.
@40dRequest frame redisplay.
@d
; Entry point for "find" command.
@d
@40dGet the search pattern.
@40dExit if none entered.
@40dDo the searching.
@40dExit if not found.
@40dRound result address down to even.
@40dStore it in current address.
@40dRequest frame redisplay.
@d
@d
; Search routine for the "find" command.
@d
@40dStart searching at start address - 2.
@40dSet the number of bytes to match.
@40dSet the search pattern address.
@40dGo to next address.
@d
@40dSee if limit address reached.
@40dIf so, return false.
@40dCompare one byte.
@40dGo to next address if mismatch.
@40dDecrement byte counter.
@40dLoop until all matched.
@40dFound: return the address where.
@d
@40dReturn false.
@d
@d
; Search/fill pattern input routine. Prompts for a pattern,
; accepts it, and returns the following:
; D0: Number of bytes in search pattern.
; D1: True if pattern entered, false if aborted.
; A0: Lower bound for search.
; A2: Upper bound for search.
; A3: Address of search pattern.
@40dPoint to "pattern? " string.
@40dPrint it.
@40dGet a number from the terminal.
@40dDid we get one?
@40dIf not, return false.
@d
@40dCompute 4-(d+1)/2, where d is the
@40dnumber of digits entered.
@d
@40dGet the number entered.
@40dGet the current address,
@40dand the limit address.
@40dReturn true.
@d
@a?fc2a2c
@8p@,12s
; Entry point for "fill" command.
@d
@40dGet the fill pattern.
@d
@40dIf one entered, go do the fill.
@40dRequest frame redisplay.
@d
@d
; Fill routine for the "fill" command.
@40dAdjust number of bytes for "dbra".
@40dInitialize loop counter.
@40dInitialize pattern address.
@40dUpper limit reached?
@40dExit if so.
@40dCopy one byte from the fill pattern.
@40dLoop until pattern done.
@40dLoop back and do pattern again.
@d
; Subroutine to print a single space.
@d
@40dCharacter code for a space.
@40dRawPutChar()
@d
@d
; Subroutine to print a single newline.
@d
@40dPoint to string below.
@40dPrint it.
@d
@d
@8p@,2s
; Subroutine to print string at address pointed to by A0, followed
; by a newline.
@d
@40dPrint the string.
@40dPrint a newline.
@d
@d
; I don't know what this is for. It's not referenced anywhere.
@d
@d
; Subroutine to get a character. Returns with the zero flag clear
; if a character was received, set otherwise.
@d
@d
@d
@d
@d
; Isdigit() type of function. Enter with a character in D0, returns
; with the zero flag set if the character is a numeric digit.
@a?fc2aac
FC2AAC cmp.b #'0',D0
@a=fc2ab0
@d
FC2AB2 cmp.b #'9',D0
@a=fc2ab6
@d
@d
@d
; Subroutine to convert an 8-bit code in D0 to a 2 character
; printable ASCII version.
; Codes 0 and 128-255 return "..", control codes are shown as "^A"
; to "^\", other characters are shown as a space, then the actual
; character.
@d
@40dMove ".." into D2.
@40dIs the code zero?
@40dIf so, return ".." and exit.
@40dIs the high bit set?
@40dIf so, return ".." and exit.
@40dMove space and null into D2.
@d
@40dIf the character is a control code,
@d
@40d Move "^" and null into D2.
@a?fc2adc
FC2ADC or.b #$40,D0 Convert to printable character.
@a=fc2ae0
@40d Put it into D2.
Endif
@40dReturn D2.
@d
@d
; ROM-Wack command execution function. Calls the command function
; given in A0, and on return, displays the current "frame" if
; requested by the command function.
@d
@40dGet the command function address.
@40dExecute the function.
@40dNeed to display frame?
@40dExit if not.
@40dGet current address.
@40dGet frame size.
@40dDisplay the frame.
@d
; Entry point for "?" command (Display multi-character commands).
@d
@40dPoint to ROM-Wack command table.
@40dGet a character.
@d
@40dIf not zero, print it, and loop
@40dback to do the next one.
@40dPrint a space.
@40dAnother zero?
@40dIf not, loop back & print next cmd.
@40dPrint a newline.
@d
@d
; ROM-Wack command dispatch table scanner.
; Enter with a character code in D0. This routine runs through the
; command table, and returns either the address of the command table
; entry matching the character, or zero.
; Each command table entry either corresponds to exactly one
; character, or to a range of characters. This may sound confusing,
; but I've commented the command table itself, so you can just look
; there and it will be perfectly obvious.
@d
@40dPoint to command table.
@d
@40dGet link to next table entry.
@40dStrip off high 8 bits.
@40dExit if no next entry.
@40dPoint ot next entry.
@40dSee if command matches table entry.
@40dExit if it does.
@40dGo to next entry if smaller.
@40dCheck if in range.
@40dGo to next entry if not.
@40dReturn table entry address.
@d
; ROM-Wack command dispatcher. Given a command character, looks it
; up in the command dispatch table and executes the command if found.
; C style entry point.
@d
; Assembler entry point.
@40dLook command up in command table.
@40dFound it?
@40dExit if not.
@d
@40dGet execution address from table
and store it on the stack.
@40dExecute the function.
@40dPop address back off the table.
@d
; Multi-character command lookup function.
; This takes a pointer to a command string in A0, and looks it up
; in the table of multi-character commands. It returns the address
; of the table entry corresponding to the command, or zero if the
; command was not found.
; C style entry point.
@d
; Assembler entry point.
@d
@d
@40dPoint at command dispatch table.
@d
@40dGet pointer to next table entry.
@40dExit if null (end of table).
@d
@40dGet pointer to string.
@40dPoint to string to match.
@40dCompare them.
@40dGot a match?
@40dKeep looking if not.
@40dReturn table address.
@d
@d
; ROM-Wack main loop: Gets a character from the user, then
; interprets it, then gets next characer, etc.
@40dGet a character from the user.
@40dStore it.
@40dProcess it.
@40dGo get next character.
; Entry point for letters, numbers and underline characters typed
; at the ROM-Wack command level. This function collects them in
; a buffer.
@40dAddress of secondary key bindings.
@40dAlready using them?
@d
; This is the first character of a multi character command, so
; clear the buffer and point to the other set of key bindings.
@40dZero characters in buffer so far.
@40dSave address of primary key bindings.
@40dPoint at secondary key bindings.
@a?fc2bbe
FC2BBE cmp.b #' ',$82(A6) Was the key a space?
@a=fc2bc4
@40dIf so, ignore it (kludge, see below).
@40dGet number of chars in buffer
@40d50 characters already?
@40dIf equal or more, ignore this one.
@40dGet command character.
@40dEcho it to the user.
@40dPoint to buffer.
@40dGet offset into buffer.
@40dStore character in buffer.
@40dIncrement buffer count.
@40dThere is unprocessed data in the
@40dbuffer, so set the flag.
; Entry point to get a number from the user.
@a?fc2bf0
FC2BF0 move.b #' ',$82(A6) Start using secondary key bindings.
@a=fc2bf6
@d
@40d Indicate that input is being
gathered for a specific function.
; Loop to read the digits. We stay in this loop, using the
; secondary (input gathering) key bindings until some command
; function switches us back to the primary ones. That means
; the user either canceled the line, or pressed RETURN.
@40d Using secondary key bindings now?
@40d Exit loop if not.
@40d Get a key from the user.
@40d Store it in "last key typed".
@40d Dispatch it as a command character.
@40d Loop.
; The input line has been processed. Either it was a command
; (and was executed), or it was a number, or it was an error.
@40d Clear "parameter input" flag.
@d
@40d Get number of digits.
@40d If no useful data in buffer (line
@40d canceled, command already executed,
@40d bad symbol...), return zero digits.
@d
; Entry point for <Space> while using the secondary key bindings.
@d
; Entry point for CTRL-U and CTRL-X. Point back to the original
; set of key bindings, and set the buffer to empty.
@40d-1 characters in buffer.
@40dRestore top-level key bindings.
@40dPrint a linefeed.
@40dBuffer contains no useful data.
@d
; Entry point for <Backspace>. This deletes one charaacter from
; the input buffer.
@40dAny characters in the buffer?
@40dIf none, cancel input.
@40dPoint to buffer.
@40dGet buffer offset.
@40dClear last character in buffer.
@40dDecrement buffer count.
@40dPoint to string below.
@40dPrint it.
@40dIs buffer now empty?
@40dIf so, cancel input.
@d
; String used to erase one character from the user's terminal.
@8p@,4s
; Entry point for <Return>.
@40dRestore previous key bindings.
@40dPoint to input buffer.
@40dGet number of characters in it.
@d
@40dIndicate that no data is in the
buffer waiting for processing.
@d
@d
; There was data in the buffer, now interpret it.
@40dZero-terminate the buffer.
@40dPoint to it.
@40dLook command up in table.
@40dBranch if not found.
@d
@40dIndicate that buffer was processed.
@40dGet the command table offset.
@40dGet execution address from table.
@40dExecute the command.
@40dClear frame redisplay flag.
@40dPop execution address of stack.
@d
; Continue here if the buffer contained a string not found
; in the command table. For a full Wack, this means it would
; either be a number, or a symbol. For ROM-Wack, it's either
; a number or an error.
@40dPoint to the string in the buffer.
@40dPoint to input number location.
@40dInterpret input buffer as a number.
@40dStore number of digits.
@d
@40dIf 0 digits (bad hex number), print
@40d"unknown symbol", indicate that no
@40ddata is waiting in the buffer, and
@40dexit.
; Continue here if number of digits was non-zero. If the number
; wasn't being gathered as a parameter to another command, make it
; the current location.
@40dParameter being gathered?
@40dExit if so.
@40dRound number down to even.
@d
@40dSet the current location to it.
@40dRequest (re)display of frame.
@d
@8p@,18s
; Hex number interpreting routine.
@d
@40dStart result at zero.
@40dStart with "-1 digits".
@d
@40dJump into the loop.
@40dEntry point for hex digits: Add 10.
@d
@40dShift previous number left by 4 bits.
@40dAdd in new digit.
@40dIncrement digit counter.
@40dGet a digit from the input string.
@40dExit if end of string reached.
@a?fc2d14
FC2D14 sub.b #'0',D0 Subtract '0'.
@a=fc2d18
@40dExit if result less than zero.
@40dGreater than 9?
@40dBranch if not.
@40dSubtract 17.
@40dExit if result less than zero.
@40dBranch if less than 7.
@d
@a?fc2d2c
FC2D2C sub.b #$20,D0 Subtract ('a' - 'A').
@a=fc2d30
@40dBranch if less than zero.
@d
@40dBranch if less than 7.
; If we branch here, the number entered contained an invalid
; hexadecimal digit.
@40dReturn "0 digits".
; Branch here if end of string reached with no invalid digits.
@40dStore result where indicated.
@40dReturn number of digits.
@d
@d
; Some sort of format string. Not used anywhere.
@a?fc2d42
@8p@,6s
; Toupper() type of function. Converts character in D0 to upper
; case if necessary.
@a?fc2d48
FC2D48 cmp.b #'a',D0 Less than 'a'?
@a=fc2d4c
@d
@a?fc2d4e
FC2D4E cmp.b #'z',D0 Greater than 'z'?
@a=fc2d52
@d
FC2D54 sub.b #$20,D0 If neither, convert to upper case.
@a=fc2d58
@d
@8p@32wPadding.
---------------------------------------------------------------------------
result = Procure( semaphore, bidMessage )
D0 A0 A1
---------------------------------------------------------------------------
; A Procure/Vacate semaphore is a message port structure plus a
; counter. A task can "lock" the semaphore, by calling Procure().
; Further attempts to lock the semaphore won't succeed until the
; original locker unlocks the semaphore by Vacate().
; A sm_Bids value of -1 indicates that the semaphore is free, zero
; indicates it's locked and no one is waiting, 1 means one task is
; waiting, etc.
; Whenever Procure() is called, a pointer to a message must be
; given. This message is enqueued on the semaphore's port if the
; semaphore is not currently available, and returned when the
; caller's turn comes up to get the lock. If the semaphore was
; available (sm_Bids = -1), the message is not used.
@40dIncrement the sm_Bids field.
@40dCheck if the semaphore was free.
@40dIf it was, store pointer to the
current locker's message
@40dand return TRUE.
@d
@40dIf it wasn't, enqueue the message
@40don the semaphore's port and return
@40dFALSE.
---------------------------------------------------------------------------
Vacate( semaphore )
A0
---------------------------------------------------------------------------
; A task which has successfully obtained a semaphore via Procure()
; calls this to release it again. The sm_Bids field is decremented.
; If it was zero and ends up at -1, nobody else was waiting to use
; the semaphore. If it ends up positive, someone else is waiting
; and we must send his bid message back to let him know he has it
; now. The first message enqueued on the semaphore's port is the
; task which has waited longest and gets it.
@40dClear pointer to locker's message.
@40dDecrement sm_Bids field.
@40dReturn if it is now -1.
@d
@40dSave semaphore pointer.
@40dDequeue the oldest bid message.
@40dRestore semaphore pointer.
@40dStore pointer to the message.
@40dExit if no message (error).
@d
@40dReplyMsg() to inform the waiting
@40dtask, then exit.
---------------------------------------------------------------------------
InitSemaphore( signalSemaphore )
A0
---------------------------------------------------------------------------
@40dPoint to the waiting task list.
@40dInitialize it to empty.
@d
@d
@d
@40dClear the ss_Owner field to null.
@40dClear the ss_NestCount to zero.
@40dSet the ss_QueueCount to -1.
@d
---------------------------------------------------------------------------
ObtainSemaphore( signalSemaphore )
A0
---------------------------------------------------------------------------
@40dForbid()
@40dIncrement the ss_QueueCount.
@d
; If the ss_Queuecount is now 0, then the semaphore was free and
; we got it. Set the pointer to the owning task, set the nesting
; level to 1, and exit.
@40dStore pointer to owning task.
@d
; The ss_Queuecount was not -1, so the semaphore was already owned
; by someone. Check if it is the calling task.
@d
@40dGet current task pointer.
@40dDo we own the semaphore already?
@40dIf so, increment nest count and exit.
; Another task owns the semaphore at the moment, so we must block.
; Since all non-running tasks must be on the TaskWait queue, we
; can't just enqueue the task on the semaphore. Instead, we build
; a temporary node on the stack, and enqueue that. Then we clear
; signal #2, and wait for someone to set it, which will occur when
; the current owner of the semaphore releases it. When we wake up,
; we deallocate the temporary node, and return to the user.
@40dReserve 12 bytes of stack space.
@40dStore pointer to this task.
@40dClear signal #2.
@40dPoint to waiting task list.
@d
@40dEnqueue the temporary list node.
@d
@40dWait for signal #2.
@40dRelease the reserved stack space.
@d
@40dIncrement the nesting count.
@40dPermit()
@d
---------------------------------------------------------------------------
ReleaseSemaphore( signalSemaphore )
A0
---------------------------------------------------------------------------
@40dDecrement the nesting count.
@40dIf zero, release the semaphore.
@40dIf it went negative, guru city!
@40dOtherwise, just decrement the
@40dss_QueueCount and exit.
; Continue here when the nesting count went to zero, and we
; therefore don't want the semaphore any more.
@40dForbid()
@40dDecrement the ss_QueueCount.
@40dIf now -1, nobody was waiting.
; The ss_Queuecount is still positive, so someone is blocked on
; the semaphore and waiting to be awakened. The first (oldest)
; entry on the semaphore's queue gets it.
@d
@40dSave semaphore pointer.
@40dPoint to semaphore's queue.
@40dRemHead()
@40dIf the queue was empty, guru city!
@d
; We have a pointer (in D0) to a node which identifies the
; waiting process (the temporary one allocated on that process's
; stack).
@d
@d
@40dGet pointer to waiting task.
@40dMake this task the ss_Owner.
@d
@40dSet waiting task's signal #2.
@d
@40dExit.
; Continue here if nobody was waiting for the semaphore. In this
; case just clear the semaphore's owner field.
@40dClear ss_Owner to null.
@40dPermit()
@d
; Put up an alert if the semaphore's nest count went negative
; (should never happen, we give away the semaphore when it reaches
; zero), or if the semaphore's queue was empty even though the
; ss_QueueCount said it isn't.
@d
@40dAlert number (fatal).
@40dGet ExecBase.
@40dPut up the alert.
@40dNever executed.
@d
---------------------------------------------------------------------------
success = AttemptSemaphore( signalSemaphore )
D0 A0
---------------------------------------------------------------------------
@40dGet pointer to current task.
@40dForbid()
@40dIncrement the ss_QueueCount.
@40dBranch if now zero.
@40dDo we own the semaphore already?
@40dBranch if true.
@40dDecrement the ss_QueueCount again.
@40dPermit()
@40dReturn FALSE.
@d
; Continue here if we were able to get the semaphore.
@40dInstall pointer to owning task.
@40dIncrement nest count.
@40dPermit()
@40dReturn TRUE.
@d
---------------------------------------------------------------------------
ObtainSemaphoreList( list )
A0
---------------------------------------------------------------------------
; This function is given a linked list of semaphore structures, and
; obtains them all. This is done in two passes. On the first pass,
; a SemaphoreRequest is enqueued on all semaphores which aren't free,
; and all the free ones are grabbed.
; On the second pass, the list is traversed again. This time, if
; we already have a semaphore, we just pass it, otherwise, we wait
; for it.
; Note: Whereas ObtainSemaphore() builds its SemaphoreRequest node
; on the task's stack, this one uses the single copy inside the
; semaphore structure. This means that a crash is sure to result
; if more than one task tries to ObtainSemaphoreList and several
; end up waiting for the same semaphore. The documentation has
; a bit more to say about this.
@d
@40dClear the "need to wait" flag.
@40dGet pointer to current task.
@40dForbid()
@d
@40dPoint to first semaphore in list.
@d
@40dEnd of list reached?
@40dExit loop if so.
@40dIncrement current semaphore's count.
@40dBranch if now zero.
@40dCheck if the semaphore is ours.
@40dBranch if true.
@40dUse the ss_MultipleLink structure
@40dto enqueue our semaphore request on
@40dthe semaphore's wait queue.
@d
@40dIndicate that we need to wait.
@40dGo to next semaphore in list.
; We got a semaphore.
@40dMake us the owner of this semaphore.
@40dIncrement its nest count.
@40dTry next semaphore in the list.
; The end of the list was reached.
@40dDid we get all the semaphores?
@40dIf so, exit.
; We have to wait for at least one semaphore in the list.
@40dGo to the start of the list.
@d
@40dEnd of list reached?
@40dIf so, exit.
@40dDo we own this semaphore?
@d
@40dIf we do, and its nest count is not
@40dzero, go on to the next one.
@40dIncrement the nest count.
@40dGo on to the next one.
; Wait for a semaphore.
@d
@40dWait for signal #2.
@40dGo back and check if we have it now.
@40dPermit()
@d
@d
---------------------------------------------------------------------------
ReleaseSemaphoreList( list )
A0
---------------------------------------------------------------------------
@d
@40dGo to the start of the list.
@40dGo to first/next node.
@40dEnd of list reached?
@40dIf so, exit.
@40dReleaseSemaphore()
@40dLoop until all done.
@d
@d
---------------------------------------------------------------------------
AddSemaphore( signalSemaphore )
A1
---------------------------------------------------------------------------
@40dInitSemaphore()
@40dPoint to global semaphore list.
@40dAdd the semaphore to the list.
---------------------------------------------------------------------------
RemSemaphore( signalSemaphore )
A1
---------------------------------------------------------------------------
@40dUnlink semaphore from whatever
list it's in.
---------------------------------------------------------------------------
signalSemaphore = FindSemaphore( name )
D0 A1
---------------------------------------------------------------------------
@40dPoint to global semaphore list.
@40dFindName()
@d
@8p@32wPadding.
---------------------------------------------------------------------------
CopyMemQuick( source, dest, size )
A0 A1 D0
---------------------------------------------------------------------------
; The caller guarantees that the source and destination are even,
; and that the transfer count is a multiple of 4. This allows us
; to skip some of the strategy below.
@40d0 bytes left over after longword copy.
@40dGo copy (at least) as longwords.
---------------------------------------------------------------------------
CopyMem( source, dest, size )
A0 A1 D0
---------------------------------------------------------------------------
; This is an interesting tutorial on efficient 68000 memory moving.
; Note that this is not necessarily the best way to move memory on
; a 68010 or 68020, since both of these processors have expanded
; features designed to make memory moving more efficient, and all
; the computational overhead of choosing the right copying method
; might not be worth it.
@40dLess than 12 bytes to copy?
@d
@40dIf so, just copy byte by byte.
; The following bit of code handles odd/even source and destination
; addresses. The following is done:
; Source and destination even: Just continue.
; Source and destination odd: Copy 1 byte. Both addresses are now
; even, so we can continue.
; Source even, dest. odd: Copy byte by byte.
; Source odd, destination even: Copy one byte, then copy the rest
; byte by byte.
; Note that there is nothing we can do if the source and destination
; differ by an odd number, i.e. are "out of phase". In such a case,
; we must copy byte by byte.
@d
@40dIs the source address odd?
@40dBranch past the following if not.
@40dCopy one byte.
@40dDecrement transfer count.
@d
@40dIs the destination address odd?
@40dIf so, transfer byte by byte.
; Both the source and destination addresses are (now) even.
; The worst that can happen now is that we have to move the data
; as individual longwords. First, compute how many bytes will
; be left over after we move the data as longwords, and save this
; value.
@40dFind how many bytes will be left
@40dif data is copied as longwords.
@40dSave this number.
; The "move multiple" approach below copies 48 bytes of data with
; only two instructions. But to use it, 48 bytes of registers need
; to be saved and restored, and so it's not worth it unless we have
; at least two 48 byte "batches" to copy.
@d
@40d96 or more bytes to move?
@40dIf not, copy as longwords.
@40dSave 12 registers on the stack.
@40dGet 48 bytes with one instruction.
@40dMove them to the destination.
@d
@40dAdd 48 to destination address.
@40dSubtract 48 from transfer count.
@40dCopy another batch if 48 or more
@40dbytes remain to be copied.
@40dRestore the 12 registers.
; Whatever the case, D0 now contains the number of bytes left over.
; Copy as many of these as possible as longwords.
@40dSee how many longwords to copy.
@40dBranch if none.
@40dAdjust for dbra.
@40dSplit up into two 16-bit values.
@d
@40dCopy the longwords.
@d
@d
; Done copying longwords. Get the number of bytes left over (0-3)
; from the stack and fall into the byte copy routine.
@40dRestore number of bytes left over.
@40dExit if none.
@40dHigh 16 bits of byte count = 0.
@40dEnter the byte copy loop.
; Copy as individual bytes, either to clean up odds and ends after
; copying in larger chunks, or because copying in larger chunks
; wasn't worth it.
@40dSplit transfer count up into two
@40d16-bit sections.
@40dEnter loop at bottom (for dbra).
@40dCopy the bytes.
@d
@d
@d
; System exception alert entry point
; ----------------------------------
; If an exception occurs or a TRAP instruction is executed while the
; CPU is in supervisor mode, we jump here, since we have no task to
; blame it on. If a task was running, we use its tc_TrapCode vector
; instead, but at powerup, the exec default for this vector is also
; initialized to point here. Later, it is stolen by some other
; part of the system so the "Software error - task held" window can
; be put up before the guru.
@40dDump the registers.
@40dPoint at exception data on the stack.
@40dGet ExecBase.
@40dIs it even?
@d
@40dIf so, point where the current task
@40dpointer probably is.
@40dGet the exception number.
@40dMask out any garbage.
; Fall into Alert(). alertNum is the exception number from the
; stack, and A5 points either to the current task pointer, or to
; the exception data on the stack.
---------------------------------------------------------------------------
Alert( alertNum, parameters )
D7 A5
---------------------------------------------------------------------------
; This routine has a relatively failsafe mechanism for getting an
; alert message up on the screen. I call this the "deferred guru".
; Right away, a signature ("HELP") is installed at location 0, and
; the guru data is stored at location $000100. If the system hangs
; now, the user will get the guru after he/she reboots manually
; via CTRL-Amiga-Amiga. If for any reason it decides it's in
; serious trouble, it will reset itself, with the same effect.
@40dDisable all interrupts.
@d
; If location 0 ALREADY contains "HELP", something is wrong, and
; no matter what the requested alert was, we reset the computer.
@40dSee if "HELP" is at location zero.
@40dIf so, unrecoverable crash.
; Store our own "deferred guru" data at 0 and $000100. Then see
; if we should use it. If ExecBase has been clobbered, or the
; stack is not working, or the unrecoverable alert bit is set,
; we blink the power light, and reset the computer. The guru will
; come up during the reboot.
@40dMove "HELP" at location 0 now.
@40dPoint at location $000100.
@40dStore alert number there.
@40dStore the parameter.
@40dGet ExecBase.
@d
; Without checking, I would assume that this is where the famous
; "Recoverable alert doesn't work with expansion memory" bug is.
; Namely, if the ExecBase structure isn't in the first 64K of
; memory, we assume that it isn't OK. With $C00000 memory, it
; will be at $C00276, and KABOOM, this fails, and the system
; resets, doing the unrecoverable alert thing via the "deferred
; alert" mechanism discussed earlier.
@40dIs ExecBase in the first 64K?
@40dIf not, unrecoverable crash.
@d
@40dCheck ExecBase complement pointer.
@d
@40dUnrecoverable crash if not valid.
@40dCheck if the stack is working by
@40dpushing a signature and trying to
@40dpop it off again.
@40dUnrecoverable crash if not working.
@40dTest high bit of alert number.
@40dBranch if unrecoverable.
; Processing for recoverable alerts. If we get this far, we have
; verified that the "dead end alert" flag was not set, that the
; ExecBase pointer and structure are probably OK, and that the
; stack is working (stack pointer pointing to RAM).
@40dPoint to ExecBase->LastAlert.
@40dStore alert number.
@40dStore parameter.
@40dCall the guru alert routine.
@40dWas the subsystem ID field zero?
@40dIf not, return to the caller.
; The alert number was of the form 0000xxxx. This means that it
; must have been a system exception. Presumably, the user has had
; the option of pressing the left or right mouse button, reflected
; in D0 as returned from the guru routine. Accordingly, we
; reset or go to the debugger.
@40dTest the guru routine's return code.
@40dRestore the registers.
@40dReset if zero flag not set.
@40dElse go to ROM-Wack.
@40dEnable interrupts if they are
@40dsupposed to be enabled.
@d
@d
; Unrecoverable system crash entry point
; --------------------------------------
; This routine blinks the power light slowly 6 times, and checks
; whether the user presses DEL on a terminal attached to the
; serial port. If DEL is received, it jumps to ROM-Wack. If
; DEL is not received, it resets the computer. This may or may
; not lead to a guru, depending on how locations 0 and $000100 were
; set up before this was called.
; Entry point for when the stack is not working (stack pointer
; clobbered). Set the stack pointer to 256K, and build a fake
; exception stack frame below it.
@40dSet stack pointer to 256K
@40dBuild fake exception stack frame.
@d
; Entry point for when the stack was working.
@40dSet CIA data direction register.
@40dSet power light to bright.
; Force the CPU into supervisor mode. If the MOVE.W #$2700,SR
; instruction bombs the first time, it will work the second time.
@40dSet privilege violation vector.
@a?fc3076
FC3076 move.w #$2700,SR Mask all maskable interrupts.
@a=fc307a
; Blink the power light slowly 6 times and look for a DEL character
; coming in through the serial port at 9600 bps. If such a character
; is received, go to the debugger.
@40dSet loop counter to 5.
@40dSet serial port for 8 bits, 9600 bps.
@d
@40dSet power light to dim.
@40dDelay.
@40dSet power light to bright.
@40dDelay.
@40dRead the serial port.
@40dClear serial port interrupt bit.
@d
@40dDid we receive a DEL character?
@40dIf not, keep blinking the light.
@40dIf DEL not pressed, reset.
; The user pressed DEL. Push the exception number on the stack
; and enter the debugger.
@d
@40dGo to ROM-Wack.
; "Deferred Guru" support routines
; --------------------------------
; The following routine is called early in the startup code. Since
; there is no stack at that point, it returns via a jump, rather
; than an RTS. It removes the "HELP" at zero, if present, and
; loads D6 and D7 with the data for ExecBase->LastAlert.
@40dLoad -1 into D6
@40dSee if location 0 contains "HELP".
@40dIf not, go back to init. code.
@40dClear location 0.
@40dLoad D6 and D7 from location $0100.
@40dGo back to init. code.
; The following subroutine is called later, after the ExecBase
; structure has been built. It writes the data determined above
; (still in D6 and D7) into ExecBase->LastAlert.
@d
@d
; Guru Alert Routine
; ------------------
; This routine puts the big red "Guru" message up on the screen,
; waits for the user to click the mouse button, then returns.
; The routine is called with the alert number and parameters stored
; at LastAlert in the ExecBase structure. This routine reads the
; alert number from there and decides what to do with it.
; An alert number of -1 means that no alert was outstanding, and
; therefore, the routine returns right away. This means that if
; it is called via the "alert.hook" mechanism, and no alert was
; pending from before the reboot, nothing will happen.
; If the alert number is not -1, an alert will be generated. The
; following algorithm is used to decide what the first string in
; the alert should say.
; IF the alert number is of the form xxxx01xxxxxxxx THEN
; Make the alert message "Not enough memory".
; ELSEIF the high bit in the alert number is clear, and the
; "general error" field is not zero THEN
; Make the alert message "Recoverable Alert".
; ELSE
; Make the alert message "Software Failure".
; ENDIF
; When the alert is finished (the user pressed the mouse button),
; the LastAlert field in ExecBase is cleared to -1, and the longword
; at location 0 is cleared to zero.
@d
@40dDelay for a while.
@d
@d
@d
@40dGet the alert number.
@40dCompare to -1 (indicates no alert).
@d
@40dExit if equal.
@40dReserve 200 bytes of stack space.
@40dPoint to base of reserved area.
; Decide which alert message to use.
@40dPoint to "Software Failure" string.
@40dGet the alert number.
@40dGet the general error number.
@40dIs it an AG_NoMemory type of alert?
@40dIf so, point to "Not enough Memory"
@40dstring.
@d
@40dIf otherwise, check the dead-end
@40dalert flag. If it is clear, but
@40dthe Subsystem/General error fields
@40dare not zero, then point to the
@40d"Recoverable Alert" string.
@40dCopy the string into the buffer.
@40dPoint to "Press left mouse button..."
@40dCopy the string into the buffer.
@40dPut a zero into the buffer.
@40dPoint to "Guru Meditation..." string.
@40dPoint to alert data.
@40dPoint to character output routine.
@40dRawDoFmt()
@40dPoint to "intuition.library".
@40dMinimum version is 0 (any).
@40dOpenLibrary()
@40dDid it open OK?
@40dIf not, skip the following.
@40dSave ExecBase.
@40dGet IntuitionBase.
@40dTell intuition that the alert # is 0.
@40dPoint to the alert string.
@40dAlert should be 40 video lines high.
@40dDisplayAlert()
@d
@40dGet IntuitionBase.
@40dRestore ExecBase.
@40dCloseLibrary()
@40dDeallocate string space on stack.
@40dRemove "HELP" at 0, if present.
@d
@40dSet LastAlert to -1.
; Set the return code. If intuition.library didn't open, A2 contains
; a non-zero address, so a non-zero return value results. If
; intuition could be called, this is the return code from the
; DisplayAlert() call.
@40dSet return code.
@d
@d
; Character output routine for RawDoFmt() while putting together
; the "Guru Meditation #..." message.
@40dPut character in buffer.
@40dZero-terminate the buffer.
@d
; Routine to copy a given string to the output buffer while
; building the guru message.
@40dPut a zero into the buffer.
@40dCopy the given string.
@d
@40dTerminate buffer with $FF.
@d
; Strings used for the guru message.
@8p@,22s
@8p@,21s
@8p@,22s
@8p@,39s
@8p@,31s
; Name used to open intuition to put up the alert.
@8p@,18s
; The "alert.hook" mechanism
; --------------------------
; This is a RomTag. At system startup, this will be found and added
; to the resident module list. Since it has the RTW_COLDSTART flag
; set, it will be initialized along with the other libraries,
; devices, etc. The RTF_AUTOINIT flag is not set, therefore, the
; code at RT_INIT is called.
; The code at RT_INIT is the Guru routine. This reads the LastAlert
; field in ExecBase. If this is not -1, then an alert is still
; outstanding (from before the reboot), and this puts it up, lets
; the user click the mouse button, then returns. The resident
; module initialization then continues, the DOS boots, and the
; system comes up.
; The purpose of all this is to allow the system to defer display
; of a guru message until after the system has been cold-started.
@8p@,13s
@a?fc323a
@8p@24wRTC_MATCHWORD (start of ROMTAG marker)
@8p@24lRT_MATCHTAG (pointer RTC_MATCHWORD)
@8p@24lRT_ENDSKIP (pointer to end of code)
@8p@24bRT_FLAGS (RTW_COLDSTART)
@8p@24bRT_VERSION (33 decimal)
@8p@24bRT_TYPE (NT_UNKNOWN)
@8p@24bRT_PRI (priority = 5)
@8p@24lRT_NAME (pointer to name)
@8p@24lRT_IDSTRING (pointer to ID string)
@8p@24lRT_INIT (execution address)
; ROM-Wack command dispatch tables ("Key bindings").
; --------------------------------------------------
; ROM-Wack can be in one of two modes when a key is pressed. It can
; be waiting for a command, or it can be in the process of gathering
; a multi character command. The two tables below decide what to do
; with the key in each case.
; Each table entry has 4 fields. These are, address of next table
; entry to try if key doesn't match this one, lowest key value for
; this table entry, highest key value (if several) or zero, address
; to jump to.
; Primary command dispatch table.
@8p@9l@3b@3b@17l^D
@8p@9l@3b@3b@17l<Return>
@8p@9l@3b@3b@17l<Tab>
@8p@9l@3b@3b@17l?
@8p@9l@3b@3b@17l.
@8p@9l@3b@3b@17l,
@8p@9l@3b@3b@17l>
@8p@9l@3b@3b@17l<
@8p@9l@3b@3b@17l<Backspace>
@8p@9l@3b@3b@17l<Space>
@8p@9l@3b@3b@17l[
@8p@9l@3b@3b@17l]
@8p@9l@3b@3b@17l:
@8p@9l@3b@3b@17l+
@8p@9l@3b@3b@17l-
@8p@9l@3b@3b@17l=
@8p@9l@3b@3b@17l!
@8p@9l@3b@3b@17l^
@8p@9l@3b@3b@17l_
@8p@9l@3b@3b@17l0 - 9
@8p@9l@3b@3b@17la - z
@8p@9l@3b@3b@17lA - Z
@8p@32lEnd of table marker.
; Secondary command dispatch table (used while a command
; and/or address is being typed).
@8p@9l@3b@3b@17l<Backspace>
@8p@9l@3b@3b@17l<Return>
@8p@9l@3b@3b@17l<CTRL-X>
@8p@9l@3b@3b@17l<CTRL-U>
@8p@9l@3b@3b@17l>
@8p@9l@3b@3b@17l<
@8p@9l@3b@3b@17l<Space>
@8p@9l@3b@3b@17l_
@8p@9l@3b@3b@17l0 - 9
@8p@9l@3b@3b@17la - z
@8p@9l@3b@3b@17lA - Z
@8p@32lEnd of table marker.
; Table of multi-character ROM-Wack commands.
@8p@,22s
@8p@,22s
@8p@,22s
@8p@,12s
; Dispatch table for multicharacter commands.
; The fields are link to next table entry, address of command string,
; an unused value (presumably from a larger version of Wack), and
; the address to jump to for that command.
@a?fc33f4
@8p@9l@9l@5w@13l"alter"
@8p@9l@9l@5w@13l"boot"
@8p@9l@9l@5w@13l"clear"
@8p@9l@9l@5w@13l"fill"
@8p@9l@9l@5w@13l"find"
@8p@9l@9l@5w@13l"go"
@8p@9l@9l@5w@13l"ig"
@8p@9l@9l@5w@13l"limit"
@8p@9l@9l@5w@13l"list"
@8p@9l@9l@5w@13l"regs"
@8p@9l@9l@5w@13l"reset"
@8p@9l@9l@5w@13l"resume"
@8p@9l@9l@5w@13l"set"
@8p@9l@9l@5w@13l"show"
@8p@9l@9l@5w@13l"user"
@8p@l
@8p@32wPadding.
; That's it. The next RomTag (for the audio.device) comes right
; after the two bytes of padding shown above.